├── .devcontainer └── devcontainer.json ├── .editorconfig ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ ├── alpha.yml │ ├── docs.yml │ ├── part-build.yml │ ├── part-compute-version.yml │ ├── part-publish.yml │ ├── part-template-build.yml │ └── release.yml ├── .gitignore ├── .lutignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Coalesce.lutconfig ├── Coalesce.sln ├── Directory.Packages.props ├── README.md ├── coalesce-vue2.json ├── coalesce-vue3.json ├── docs ├── .vitepress │ ├── components │ │ ├── CodeTabs.vue │ │ ├── Prop.vue │ │ └── SiteFooter.vue │ ├── config.mts │ ├── importMdPlugin.ts │ ├── linkcheck-skip-file.txt │ └── theme │ │ ├── index.ts │ │ └── style.scss ├── .vscode │ └── settings.json ├── concepts │ ├── include-tree.md │ └── includes.md ├── cspell.json ├── history.md ├── index.md ├── introduction.md ├── modeling │ ├── model-components │ │ ├── attributes.md │ │ ├── attributes │ │ │ ├── client-validation.md │ │ │ ├── coalesce.md │ │ │ ├── controller-action.md │ │ │ ├── controller.md │ │ │ ├── create-controller.md │ │ │ ├── date-type.md │ │ │ ├── default-order-by.md │ │ │ ├── dto-includes-excludes.md │ │ │ ├── execute.md │ │ │ ├── hidden.md │ │ │ ├── inject.md │ │ │ ├── internal-use.md │ │ │ ├── list-text.md │ │ │ ├── load-from-data-source.md │ │ │ ├── many-to-many.md │ │ │ ├── restrict.md │ │ │ ├── search.md │ │ │ └── security-attribute.md │ │ ├── behaviors.md │ │ ├── data-sources.md │ │ ├── methods.md │ │ └── properties.md │ └── model-types │ │ ├── crud.md │ │ ├── dtos.md │ │ ├── entities.md │ │ ├── external-types.md │ │ ├── services.md │ │ └── standalone-entities.md ├── package-lock.json ├── package.json ├── public │ ├── coalesce-horizontal-color.svg │ ├── coalesce-icon-color.svg │ ├── ef-logo.svg │ ├── favicon.ico │ ├── intellitect-text-black.svg │ ├── intellitect-text-white.svg │ ├── net-logo.svg │ ├── ts-logo-128.svg │ ├── ts-logo-512.svg │ ├── vite-logo.svg │ └── vue-logo.svg ├── stacks │ ├── agnostic │ │ ├── dtos.md │ │ ├── generation.md │ │ └── getting-started-modeling.md │ └── vue │ │ ├── TemplateBuilder.vue │ │ ├── coalesce-vue-vuetify │ │ ├── components │ │ │ ├── c-admin-audit-log-page.md │ │ │ ├── c-admin-display.md │ │ │ ├── c-admin-editor-page.md │ │ │ ├── c-admin-editor.md │ │ │ ├── c-admin-method.md │ │ │ ├── c-admin-methods.md │ │ │ ├── c-admin-table-page.md │ │ │ ├── c-admin-table-toolbar.md │ │ │ ├── c-admin-table.md │ │ │ ├── c-datetime-picker.md │ │ │ ├── c-display.md │ │ │ ├── c-input.md │ │ │ ├── c-list-filters.md │ │ │ ├── c-list-page-size.md │ │ │ ├── c-list-page.md │ │ │ ├── c-list-pagination.md │ │ │ ├── c-list-range-display.md │ │ │ ├── c-loader-status.md │ │ │ ├── c-select-many-to-many.md │ │ │ ├── c-select-string-value.md │ │ │ ├── c-select-values.md │ │ │ ├── c-select.md │ │ │ └── c-table.md │ │ └── overview.md │ │ ├── getting-started.md │ │ ├── layers │ │ ├── api-clients.md │ │ ├── metadata.md │ │ ├── models.md │ │ └── viewmodels.md │ │ ├── overview.md │ │ └── vue2-to-vue3.md └── topics │ ├── audit-logging.md │ ├── coalesce-json.md │ ├── coalesce-swashbuckle-with.jpg │ ├── coalesce-swashbuckle-without.jpg │ ├── coalesce-swashbuckle.md │ ├── immutability.md │ ├── security-overview.jpg │ ├── security.md │ ├── startup.md │ └── upgrading.md ├── license.txt ├── playground ├── Coalesce.Domain │ ├── AppDbContext.cs │ ├── AuditLog.cs │ ├── BuildingMigrations.txt │ ├── Case.cs │ ├── CaseDto.cs │ ├── CaseDtoStandalone.cs │ ├── CaseProduct.cs │ ├── CaseStandalone.cs │ ├── CaseSummary.cs │ ├── Coalesce.Domain.csproj │ ├── Company.cs │ ├── External │ │ └── DevTeam.cs │ ├── GenFuBaseValueGenerator.cs │ ├── Log.cs │ ├── Migrations │ │ ├── 20151209212405_InitialDb.Designer.cs │ │ ├── 20151209212405_InitialDb.cs │ │ ├── 20151213005147_Dates.Designer.cs │ │ ├── 20151213005147_Dates.cs │ │ ├── 20151217034901_PersonStats.Designer.cs │ │ ├── 20151217034901_PersonStats.cs │ │ ├── 20151222040400_DevTeam.Designer.cs │ │ ├── 20151222040400_DevTeam.cs │ │ ├── 20160104203757_ChangedDevTeamToDevTeamAssigned.Designer.cs │ │ ├── 20160104203757_ChangedDevTeamToDevTeamAssigned.cs │ │ ├── 20160811224626_MakePersonStatsIdNullable.Designer.cs │ │ ├── 20160811224626_MakePersonStatsIdNullable.cs │ │ ├── 20180116002515_AddTimeSpan.Designer.cs │ │ ├── 20180116002515_AddTimeSpan.cs │ │ ├── 20180205233522_AddCompanyIsDeletedFlag.Designer.cs │ │ ├── 20180205233522_AddCompanyIsDeletedFlag.cs │ │ ├── 20180226172924_Testing.Designer.cs │ │ ├── 20180226172924_Testing.cs │ │ ├── 20180530231723_FixUniqueIdNameCollisionWithSqlType.Designer.cs │ │ ├── 20180530231723_FixUniqueIdNameCollisionWithSqlType.cs │ │ ├── 20190214041111_Attachments.Designer.cs │ │ ├── 20190214041111_Attachments.cs │ │ ├── 20190829225735_MissingMigration.Designer.cs │ │ ├── 20190829225735_MissingMigration.cs │ │ ├── 20200714190517_AddZipCode.Designer.cs │ │ ├── 20200714190517_AddZipCode.cs │ │ ├── 20201218222929_AddFakeLogTable.Designer.cs │ │ ├── 20201218222929_AddFakeLogTable.cs │ │ ├── 20220304211436_FileRework.Designer.cs │ │ ├── 20220304211436_FileRework.cs │ │ ├── 20220307191439_AddAttachmentType.Designer.cs │ │ ├── 20220307191439_AddAttachmentType.cs │ │ ├── 20220901172633_MoreFields.Designer.cs │ │ ├── 20220901172633_MoreFields.cs │ │ ├── 20231024221657_AddAuditLogging.Designer.cs │ │ ├── 20231024221657_AddAuditLogging.cs │ │ ├── 20231114001120_AddAuditLogDescription.Designer.cs │ │ ├── 20231114001120_AddAuditLogDescription.cs │ │ ├── 20240122232554_AddCollectionOfStrings.Designer.cs │ │ ├── 20240122232554_AddCollectionOfStrings.cs │ │ ├── 20240806165843_AddPrimitiveCollections.Designer.cs │ │ ├── 20240806165843_AddPrimitiveCollections.cs │ │ ├── 20241007205723_AddUniqueIndex.Designer.cs │ │ ├── 20241007205723_AddUniqueIndex.cs │ │ ├── 20241007220500_RemoveCascadeDelete.Designer.cs │ │ ├── 20241007220500_RemoveCascadeDelete.cs │ │ └── AppDbContextModelSnapshot.cs │ ├── MyDataSource.cs │ ├── Person.cs │ ├── PersonLocation.cs │ ├── PersonStats.cs │ ├── Product.cs │ ├── Program.cs │ ├── SampleData.cs │ ├── Services │ │ └── WeatherService.cs │ ├── StandaloneReadWrite.cs │ ├── StandaloneReadonly.cs │ ├── TemporaryDbContextFactory.cs │ ├── WebShared │ │ └── DemoMiddleware.cs │ └── ZipCode.cs ├── Coalesce.Web.Vue2 │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── Api │ │ └── Generated │ │ │ ├── AuditLogController.g.cs │ │ │ ├── CaseController.g.cs │ │ │ ├── CaseDtoController.g.cs │ │ │ ├── CaseDtoStandaloneController.g.cs │ │ │ ├── CaseProductController.g.cs │ │ │ ├── CaseStandaloneController.g.cs │ │ │ ├── CompanyController.g.cs │ │ │ ├── LogController.g.cs │ │ │ ├── PersonController.g.cs │ │ │ ├── ProductController.g.cs │ │ │ ├── StandaloneReadCreateController.g.cs │ │ │ ├── StandaloneReadWriteController.g.cs │ │ │ ├── StandaloneReadonlyController.g.cs │ │ │ ├── WeatherServiceController.g.cs │ │ │ └── ZipCodeController.g.cs │ ├── Coalesce.Web.Vue2.csproj │ ├── Controllers │ │ └── HomeController.cs │ ├── Models │ │ └── Generated │ │ │ ├── AuditLogDto.g.cs │ │ │ ├── AuditLogPropertyDto.g.cs │ │ │ ├── CaseDto.g.cs │ │ │ ├── CaseProductDto.g.cs │ │ │ ├── CaseStandaloneDto.g.cs │ │ │ ├── CaseSummaryDto.g.cs │ │ │ ├── CompanyDto.g.cs │ │ │ ├── DevTeamDto.g.cs │ │ │ ├── LocationDto.g.cs │ │ │ ├── LogDto.g.cs │ │ │ ├── PersonCriteriaDto.g.cs │ │ │ ├── PersonDto.g.cs │ │ │ ├── PersonLocationDto.g.cs │ │ │ ├── PersonStatsDto.g.cs │ │ │ ├── ProductDetailsDto.g.cs │ │ │ ├── ProductDto.g.cs │ │ │ ├── StandaloneReadCreateDto.g.cs │ │ │ ├── StandaloneReadWriteDto.g.cs │ │ │ ├── StandaloneReadonlyDto.g.cs │ │ │ ├── StreetAddressDto.g.cs │ │ │ ├── WeatherDataDto.g.cs │ │ │ └── ZipCodeDto.g.cs │ ├── Program.cs │ ├── Startup.cs │ ├── Views │ │ ├── Shared │ │ │ ├── Error.cshtml │ │ │ └── _Layout.cshtml │ │ ├── _ViewImports.cshtml │ │ └── _ViewStart.cshtml │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── favicon.ico │ ├── src │ │ ├── api-clients.g.ts │ │ ├── components │ │ │ ├── app.vue │ │ │ └── test.vue │ │ ├── css │ │ │ └── site.scss │ │ ├── main.ts │ │ ├── metadata.g.ts │ │ ├── models.g.ts │ │ ├── viewmodels.g.ts │ │ └── vue-shim.d.ts │ ├── tsconfig.json │ ├── tslint.json │ └── vite.config.ts ├── Coalesce.Web.Vue3 │ ├── .eslintrc.js │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── Api │ │ └── Generated │ │ │ ├── AuditLogController.g.cs │ │ │ ├── CaseController.g.cs │ │ │ ├── CaseDtoController.g.cs │ │ │ ├── CaseDtoStandaloneController.g.cs │ │ │ ├── CaseProductController.g.cs │ │ │ ├── CaseStandaloneController.g.cs │ │ │ ├── CompanyController.g.cs │ │ │ ├── LogController.g.cs │ │ │ ├── PersonController.g.cs │ │ │ ├── ProductController.g.cs │ │ │ ├── StandaloneReadCreateController.g.cs │ │ │ ├── StandaloneReadWriteController.g.cs │ │ │ ├── StandaloneReadonlyController.g.cs │ │ │ ├── WeatherServiceController.g.cs │ │ │ └── ZipCodeController.g.cs │ ├── Coalesce.Web.Vue3.csproj │ ├── Controllers │ │ └── HomeController.cs │ ├── Models │ │ └── Generated │ │ │ ├── AuditLogDto.g.cs │ │ │ ├── AuditLogPropertyDto.g.cs │ │ │ ├── CaseDto.g.cs │ │ │ ├── CaseProductDto.g.cs │ │ │ ├── CaseStandaloneDto.g.cs │ │ │ ├── CaseSummaryDto.g.cs │ │ │ ├── CompanyDto.g.cs │ │ │ ├── DevTeamDto.g.cs │ │ │ ├── LocationDto.g.cs │ │ │ ├── LogDto.g.cs │ │ │ ├── PersonCriteriaDto.g.cs │ │ │ ├── PersonDto.g.cs │ │ │ ├── PersonLocationDto.g.cs │ │ │ ├── PersonStatsDto.g.cs │ │ │ ├── ProductDetailsDto.g.cs │ │ │ ├── ProductDto.g.cs │ │ │ ├── StandaloneReadCreateDto.g.cs │ │ │ ├── StandaloneReadWriteDto.g.cs │ │ │ ├── StandaloneReadonlyDto.g.cs │ │ │ ├── StreetAddressDto.g.cs │ │ │ ├── WeatherDataDto.g.cs │ │ │ └── ZipCodeDto.g.cs │ ├── Program.cs │ ├── Views │ │ ├── Shared │ │ │ ├── Error.cshtml │ │ │ └── _Layout.cshtml │ │ ├── _ViewImports.cshtml │ │ └── _ViewStart.cshtml │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── favicon.ico │ ├── src │ │ ├── App.vue │ │ ├── api-clients.g.ts │ │ ├── assets │ │ │ └── logo.svg │ │ ├── components │ │ │ ├── Examples.vue │ │ │ ├── HelloWorld.vue │ │ │ ├── test-setup.vue │ │ │ └── test.vue │ │ ├── css │ │ │ └── site.scss │ │ ├── env.d.ts │ │ ├── examples │ │ │ ├── c-datetime-picker │ │ │ │ └── general.vue │ │ │ ├── c-input │ │ │ │ └── general.vue │ │ │ ├── c-select-many-to-many │ │ │ │ ├── binding.vue │ │ │ │ └── styling.vue │ │ │ └── c-select │ │ │ │ ├── multiple-binding.vue │ │ │ │ ├── single-binding.vue │ │ │ │ └── styling.vue │ │ ├── main.ts │ │ ├── metadata.g.ts │ │ ├── models.g.ts │ │ ├── viewmodels.g.ts │ │ └── worker.ts │ ├── tsconfig.json │ └── vite.config.ts └── Directory.Build.props ├── src ├── Cli.Common.props ├── Common.props ├── Directory.Build.props ├── IntelliTect.Coalesce.AuditLogging.Tests │ ├── AuditConfigurationTests.cs │ ├── AuditTests.cs │ ├── IntelliTect.Coalesce.AuditLogging.Tests.csproj │ ├── SqlServerAuditTests.cs │ ├── TestDbContext.cs │ └── Usings.cs ├── IntelliTect.Coalesce.AuditLogging │ ├── Configuration │ │ ├── AuditLoggingBuilder.cs │ │ ├── AuditOptions.cs │ │ └── DbContextOptionsBuilderExtensions.cs │ ├── DefaultAuditOperationContext.cs │ ├── IAuditLogDbContext.cs │ ├── IAuditOperationContext.cs │ ├── IntelliTect.Coalesce.AuditLogging.csproj │ ├── Internal │ │ ├── AuditExtension.cs │ │ ├── AuditInterceptor.cs │ │ └── CoalesceAudit.cs │ └── Models │ │ ├── AuditConfiguration.cs │ │ ├── AuditEntry.cs │ │ ├── AuditEntryProperty.cs │ │ ├── AuditEntryState.cs │ │ ├── AuditLogProperty.cs │ │ ├── DefaultAuditLog.cs │ │ └── IAuditLog.cs ├── IntelliTect.Coalesce.CodeGeneration.Api │ ├── BaseGenerators │ │ └── ApiController.cs │ ├── Generators │ │ ├── ClassDto.cs │ │ ├── Controllers.cs │ │ ├── ModelApiController.cs │ │ ├── Models.cs │ │ └── ServiceApiController.cs │ ├── IntelliTect.Coalesce.CodeGeneration.Api.csproj │ ├── MvcCodeGenerationExtensions.cs │ └── Properties │ │ └── launchSettings.json ├── IntelliTect.Coalesce.CodeGeneration.Tests │ ├── .gitignore │ ├── CodeGenTestBase.cs │ ├── DataSourceModelBinderTests.cs │ ├── IntelliTect.Coalesce.CodeGeneration.Tests.csproj │ └── TargetClassesFullGenerationTest.cs ├── IntelliTect.Coalesce.CodeGeneration.Vue │ ├── Generators │ │ ├── Controllers.cs │ │ ├── Scripts.cs │ │ └── Scripts │ │ │ ├── TsApiClients.cs │ │ │ ├── TsMetadata.cs │ │ │ ├── TsModels.cs │ │ │ └── TsViewModels.cs │ ├── IntelliTect.Coalesce.CodeGeneration.Vue.csproj │ ├── Properties │ │ └── launchSettings.json │ ├── Utils │ │ ├── VueHelperExtensions.cs │ │ └── VueType.cs │ └── VueSuite.cs ├── IntelliTect.Coalesce.CodeGeneration │ ├── Analysis │ │ ├── Base │ │ │ ├── IProjectContextFactory.cs │ │ │ ├── ProjectContext.cs │ │ │ └── TypeLocator.cs │ │ ├── MsBuild │ │ │ ├── MsBuildProjectContext.cs │ │ │ ├── MsBuildProjectContextBuilder.cs │ │ │ ├── ProjectReferenceInformationProvider.cs │ │ │ └── Targets │ │ │ │ ├── Imports.targets │ │ │ │ ├── build │ │ │ │ └── IntelliTect.Coalesce.CodeGeneration.Analysis.MsBuild.targets │ │ │ │ └── buildMultiTargeting │ │ │ │ └── IntelliTect.Coalesce.CodeGeneration.Analysis.MsBuild.targets │ │ ├── NpmDependencyAnalayzer.cs │ │ ├── ProjectAnalysisException.cs │ │ ├── Reflection │ │ │ ├── ReflectionProjectContext.cs │ │ │ ├── ReflectionProjectContextFactory.cs │ │ │ └── ReflectionTypeLocator.cs │ │ └── Roslyn │ │ │ ├── RoslynProjectContext.cs │ │ │ ├── RoslynProjectContextFactory.cs │ │ │ └── RoslynTypeLocator.cs │ ├── Configuration │ │ └── CoalesceConfiguration.cs │ ├── Generation │ │ ├── Cleaners │ │ │ ├── DirectoryCleaner.cs │ │ │ ├── FileCleaner.cs │ │ │ └── ICleaner.cs │ │ ├── GenerationContext.cs │ │ ├── GenerationExecutor.cs │ │ ├── Generators │ │ │ ├── CompositeGenerator.cs │ │ │ ├── FileGenerator.cs │ │ │ ├── Generator.cs │ │ │ ├── GeneratorConfigAttribute.cs │ │ │ ├── GeneratorExtensions.cs │ │ │ ├── IGenerator.cs │ │ │ ├── StaticFileGenerator.cs │ │ │ ├── StringBuilderCSharpGenerator.cs │ │ │ └── StringBuilderFileGenerator.cs │ │ └── Services │ │ │ ├── CompositeGeneratorServices.cs │ │ │ └── GeneratorServices.cs │ ├── IntelliTect.Coalesce.CodeGeneration.csproj │ ├── Properties │ │ └── launchSettings.json │ ├── Templating │ │ ├── Resolution │ │ │ ├── IResolvedTemplate.cs │ │ │ ├── ITemplateResolver.cs │ │ │ ├── ResolvedFileSystemTemplate.cs │ │ │ ├── ResolvedManifestResourceTemplate.cs │ │ │ └── TemplateResolver.cs │ │ └── TemplateDescriptor.cs │ └── Utilities │ │ ├── ApplicationTimer.cs │ │ ├── FileUtilities.cs │ │ └── SimpleConsoleLogger.cs ├── IntelliTect.Coalesce.DotnetCliTool │ └── IntelliTect.Coalesce.DotnetCliTool.csproj ├── IntelliTect.Coalesce.DotnetTool │ ├── IntelliTect.Coalesce.DotnetTool.csproj │ └── Program.cs ├── IntelliTect.Coalesce.Swashbuckle.Tests │ ├── IntelliTect.Coalesce.Swashbuckle.Tests.csproj │ ├── OpenApiDocumentTests.cs │ └── OpenApiFixture.cs ├── IntelliTect.Coalesce.Swashbuckle │ ├── CoalesceApiOperationFilter.cs │ ├── CoalesceApiSchemaFilter.cs │ ├── CoalesceDocumentFilter.cs │ ├── CoalesceSwashbuckleExtensions.cs │ └── IntelliTect.Coalesce.Swashbuckle.csproj ├── IntelliTect.Coalesce.Tests │ ├── Fixtures │ │ └── TestDbContextFixture.cs │ ├── IntelliTect.Coalesce.Tests.csproj │ ├── TargetClasses │ │ ├── .editorconfig │ │ ├── Bools.cs │ │ ├── CaseDtoStandalone.cs │ │ ├── ComplexInheritance.cs │ │ ├── Github31.cs │ │ ├── Ordering.cs │ │ ├── SecurityTargets.cs │ │ ├── StandaloneReadWrite.cs │ │ ├── StandaloneReadonly.cs │ │ ├── TestDbContext │ │ │ ├── AbstractModel.cs │ │ │ ├── Case.cs │ │ │ ├── CaseProduct.cs │ │ │ ├── Company.cs │ │ │ ├── ComplexModel.cs │ │ │ ├── ComplexModelDependent.cs │ │ │ ├── EnumPk.cs │ │ │ ├── ExternalTypes.cs │ │ │ ├── InternalUseClass.cs │ │ │ ├── MultipleParents.cs │ │ │ ├── OneToOne.cs │ │ │ ├── Person.cs │ │ │ ├── Product.cs │ │ │ ├── Records.cs │ │ │ ├── RecursiveHierarchy.cs │ │ │ ├── RequiredAndInitModel.cs │ │ │ ├── RoleNames.cs │ │ │ ├── Sibling.cs │ │ │ ├── StringIdentity.cs │ │ │ ├── Test.cs │ │ │ ├── TestDbContext.cs │ │ │ └── ZipCode.cs │ │ ├── ValidationTarget.cs │ │ └── WeatherService.cs │ ├── Tests │ │ ├── Api │ │ │ ├── Behaviors │ │ │ │ └── SqlServerExceptionResultTests.cs │ │ │ ├── DataSources │ │ │ │ ├── ProjectedStandaloneEntityTests.cs │ │ │ │ ├── StandardBehaviorsTests.cs │ │ │ │ └── StandardDataSourceTests.cs │ │ │ └── SearchTests.cs │ │ ├── ApplicationTests.cs │ │ ├── ClonerTest.cs │ │ ├── EntityStampingTests.cs │ │ ├── Mapping │ │ │ └── IncludeTreeTests.cs │ │ ├── Models │ │ │ ├── ItemResultTests.cs │ │ │ └── ListResultTests.cs │ │ ├── Security │ │ │ ├── ClassSecurityTests.cs │ │ │ └── PropertySecurityTests.cs │ │ ├── TypeDefinition │ │ │ ├── ClassViewModelTests.cs │ │ │ ├── MethodViewModelTests.cs │ │ │ ├── PropertyViewModelTests.cs │ │ │ └── TypeViewModelTests.cs │ │ └── Utilities │ │ │ └── StringExtensionTests.cs │ ├── Util │ │ ├── AssortedTestingExtensions.cs │ │ ├── ClassViewModelData.cs │ │ ├── ClassViewModelDataAttribute.cs │ │ ├── ClassViewModelDataTest.cs │ │ ├── PropertyViewModelData.cs │ │ ├── PropertyViewModelDataAttribute.cs │ │ ├── ReflectionRepositoryFactory.cs │ │ └── TestDto.cs │ └── xunit.runner.json ├── IntelliTect.Coalesce.Vue │ ├── DevMiddleware │ │ ├── CoalesceVueApplicationBuilderExtensions.cs │ │ ├── EventedStreamReader.cs │ │ ├── EventedStreamStringReader.cs │ │ ├── NodeScriptRunner.cs │ │ ├── ViteDevelopmentServerMiddleware.cs │ │ └── ViteServerOptions.cs │ └── IntelliTect.Coalesce.Vue.csproj ├── IntelliTect.Coalesce │ ├── Api │ │ ├── Behaviors │ │ │ ├── BehaviorsFactory.cs │ │ │ ├── BehaviorsModelBinder.cs │ │ │ ├── BehaviorsModelBinderProvider.cs │ │ │ ├── IBehaviors.cs │ │ │ ├── IBehaviorsFactory.cs │ │ │ ├── IEntityFrameworkBehaviors.cs │ │ │ ├── SaveKind.cs │ │ │ ├── StandardBehaviors`1.cs │ │ │ └── StandardBehaviors`2.cs │ │ ├── BulkSave │ │ │ ├── BulkSaveRequest.cs │ │ │ ├── BulkSaveRequestItem.cs │ │ │ └── BulkSaveRequestItemConverter.cs │ │ ├── Controllers │ │ │ ├── ApiActionFilter.cs │ │ │ ├── BaseApiController.cs │ │ │ ├── CoalesceJsonReferenceHandler.cs │ │ │ ├── ControllerHelpers.cs │ │ │ └── IApiActionFilter.cs │ │ ├── CrudContext.cs │ │ ├── CrudStrategy │ │ │ ├── CrudStrategyModelBinder.cs │ │ │ └── IStandardCrudStrategy.cs │ │ ├── DataSources │ │ │ ├── DataSourceFactory.cs │ │ │ ├── DataSourceModelBinder.cs │ │ │ ├── DataSourceModelBinderProvider.cs │ │ │ ├── DataSourceNotFoundException.cs │ │ │ ├── DefaultDataSourceAttribute.cs │ │ │ ├── IDataSource.cs │ │ │ ├── IDataSourceFactory.cs │ │ │ ├── IEntityFrameworkDataSource.cs │ │ │ ├── IResultTransformer.cs │ │ │ ├── ProjectedDtoDataSource.cs │ │ │ ├── QueryableDataSourceBase`1.cs │ │ │ ├── StandardDataSource`1.cs │ │ │ └── StandardDataSource`2.cs │ │ ├── DeclaredForAttribute.cs │ │ ├── OpenApi │ │ │ ├── CoalesceApiDescriptionProvider.cs │ │ │ └── CoalesceApiOperationFilter.cs │ │ └── Parameters │ │ │ ├── DataSourceParameters.cs │ │ │ ├── FilterParameters.cs │ │ │ ├── IDataSourceParameters.cs │ │ │ ├── IFilterParameters.cs │ │ │ ├── IListParameters.cs │ │ │ ├── ListParameters.cs │ │ │ └── SortDirection.cs │ ├── Application │ │ ├── CoalesceApplicationBuilderExtensions.cs │ │ ├── CoalesceConfigurationAttribute.cs │ │ ├── CoalesceOptions.cs │ │ ├── CoalesceServiceBuilder.cs │ │ ├── CoalesceServiceCollectionExtensions.cs │ │ ├── DefaultTimeZoneResolver.cs │ │ ├── ITimeZoneResolver.cs │ │ └── SecurityOverview.html │ ├── DTOs │ │ ├── DtoExtensions.cs │ │ └── IClassDto.cs │ ├── DataAnnotations │ │ ├── ClientValidationAttribute.cs │ │ ├── CoalesceAttribute.cs │ │ ├── ControllerActionAttribute.cs │ │ ├── ControllerAttribute.cs │ │ ├── CreateAttribute.cs │ │ ├── CreateControllerAttribute.cs │ │ ├── DateTypeAttribute.cs │ │ ├── DefaultOrderByAttribute.cs │ │ ├── DeleteAttribute.cs │ │ ├── DtoExcludesAttribute.cs │ │ ├── DtoIncludesAttribute.cs │ │ ├── EditAttribute.cs │ │ ├── ExecuteAttribute.cs │ │ ├── HiddenAttribute.cs │ │ ├── IMappingRestriction.cs │ │ ├── InjectAttribute.cs │ │ ├── InternalUseAttribute.cs │ │ ├── ListTextAttribute.cs │ │ ├── LoadFromDataSourceAttribute.cs │ │ ├── ManyToManyAttribute.cs │ │ ├── ReadAttribute.cs │ │ ├── ReadOnlyApiAttribute.cs │ │ ├── RestrictAttribute.cs │ │ ├── SearchAttribute.cs │ │ ├── SecurityAttribute.cs │ │ ├── ServiceAttribute.cs │ │ └── StandaloneEntityAttribute.cs │ ├── Helpers │ │ ├── Cloner.cs │ │ ├── EnumExtensions.cs │ │ ├── QueryableExtensions.cs │ │ └── Search │ │ │ ├── SearchableCollectionProperty.cs │ │ │ ├── SearchableObjectProperty.cs │ │ │ ├── SearchableProperty.cs │ │ │ └── SearchableValueProperty.cs │ ├── IntelliTect.Coalesce.csproj │ ├── Mapping │ │ ├── IMappingContext.cs │ │ ├── IncludeTree │ │ │ ├── IIncludedSeparatelyQueryable.cs │ │ │ ├── IncludeTree.cs │ │ │ ├── IncludeTreeExtensions.cs │ │ │ └── IncludedSeparatelyQueryable.cs │ │ ├── Mapper.cs │ │ └── MappingContext.cs │ ├── Models │ │ ├── ApiResult.cs │ │ ├── File.cs │ │ ├── FileParameter.cs │ │ ├── GeneratedDto.cs │ │ ├── IFile.cs │ │ ├── ItemResult.cs │ │ ├── ListResult.cs │ │ ├── QueryableByteStream.cs │ │ └── ValidationIssue.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Stamping │ │ ├── StampInterceptor.cs │ │ └── StampingDbContextOptionsBuilderExtensions.cs │ ├── TypeDefinition │ │ ├── ClassViewModel.cs │ │ ├── EnumMember.cs │ │ ├── Enums │ │ │ └── PropertyRole.cs │ │ ├── Helpers │ │ │ ├── IAttributeProvider.cs │ │ │ ├── ReflectionExtensions.cs │ │ │ └── SymbolExtensions.cs │ │ ├── ImplicitParameterViewModel.cs │ │ ├── MethodReturnViewModel.cs │ │ ├── MethodViewModel.cs │ │ ├── ParameterViewModel.cs │ │ ├── PropertyViewModel.cs │ │ ├── Reflection │ │ │ ├── ReflectionClassViewModel.cs │ │ │ ├── ReflectionMethodViewModel.cs │ │ │ ├── ReflectionParameterViewModel.cs │ │ │ ├── ReflectionPropertyViewModel.cs │ │ │ └── ReflectionTypeViewModel.cs │ │ ├── ReflectionRepository.cs │ │ ├── Security │ │ │ ├── ClassSecurityInfo.cs │ │ │ ├── MethodSecurityInfo.cs │ │ │ ├── PropertySecurityInfo.cs │ │ │ ├── PropertySecurityPermission.cs │ │ │ ├── SecurityPermission.cs │ │ │ └── SecurityPermissionBase.cs │ │ ├── Symbol │ │ │ ├── SymbolClassViewModel.cs │ │ │ ├── SymbolMethodViewModel.cs │ │ │ ├── SymbolParameterViewModel.cs │ │ │ ├── SymbolPropertyViewModel.cs │ │ │ └── SymbolTypeViewModel.cs │ │ ├── TypeDiscriminator.cs │ │ ├── TypeViewModel.cs │ │ └── ValueViewModel.cs │ ├── TypeUsage │ │ ├── CrudStrategyTypeUsage.cs │ │ ├── DbContextTypeUsage.cs │ │ └── EntityTypeUsage.cs │ ├── Utilities │ │ ├── CSharpCodeBuilder.cs │ │ ├── CSharpUtilities.cs │ │ ├── CodeBuilder.cs │ │ ├── ConcurrentHashSet.cs │ │ ├── EntityFrameworkServiceProvider.cs │ │ ├── ExpressionExtensions.cs │ │ ├── FilesystemExtensions.cs │ │ ├── HtmlCodeBuilder.cs │ │ ├── LazyValue.cs │ │ ├── ReadOnlyHashSet.cs │ │ ├── StringExtensions.cs │ │ └── TypeScriptCodeBuilder.cs │ └── Validation │ │ ├── ValidateContext.cs │ │ ├── ValidationHelper.cs │ │ └── ValidationResult.cs ├── coalesce-vue-vuetify2 │ ├── .browserslistrc │ ├── .editorconfig │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── README.md │ ├── dist │ │ └── cjs │ │ │ └── package.json │ ├── lib │ │ └── cjs │ │ │ └── package.json │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ └── index.html │ ├── rollup.config.ts │ ├── src │ │ ├── build.ts │ │ ├── components │ │ │ ├── admin │ │ │ │ ├── c-admin-audit-log-page.vue │ │ │ │ ├── c-admin-display.ts │ │ │ │ ├── c-admin-editor-page.vue │ │ │ │ ├── c-admin-editor.vue │ │ │ │ ├── c-admin-method.vue │ │ │ │ ├── c-admin-methods.vue │ │ │ │ ├── c-admin-table-page.vue │ │ │ │ ├── c-admin-table-toolbar.vue │ │ │ │ └── c-admin-table.vue │ │ │ ├── c-metadata-component.ts │ │ │ ├── display │ │ │ │ ├── c-display.ts │ │ │ │ ├── c-list-range-display.vue │ │ │ │ ├── c-loader-status.vue │ │ │ │ └── c-table.vue │ │ │ ├── index.ts │ │ │ └── input │ │ │ │ ├── c-datetime-picker.vue │ │ │ │ ├── c-input-props-provider.vue │ │ │ │ ├── c-input.ts │ │ │ │ ├── c-list-filters.vue │ │ │ │ ├── c-list-page-size.vue │ │ │ │ ├── c-list-page.vue │ │ │ │ ├── c-list-pagination.vue │ │ │ │ ├── c-select-many-to-many.vue │ │ │ │ ├── c-select-string-value.vue │ │ │ │ ├── c-select-values.vue │ │ │ │ └── c-select.vue │ │ ├── index.dist.ts │ │ ├── index.ts │ │ ├── shared.scss │ │ ├── shims-vue.d.ts │ │ └── util.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── coalesce-vue-vuetify3 │ ├── .editorconfig │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── build.ts │ │ ├── components │ │ │ ├── admin │ │ │ │ ├── c-admin-audit-log-page.vue │ │ │ │ ├── c-admin-display.vue │ │ │ │ ├── c-admin-editor-page.vue │ │ │ │ ├── c-admin-editor.spec.tsx │ │ │ │ ├── c-admin-editor.vue │ │ │ │ ├── c-admin-method.spec.tsx │ │ │ │ ├── c-admin-method.vue │ │ │ │ ├── c-admin-methods.spec.tsx │ │ │ │ ├── c-admin-methods.vue │ │ │ │ ├── c-admin-table-page.vue │ │ │ │ ├── c-admin-table-toolbar.vue │ │ │ │ ├── c-admin-table.vue │ │ │ │ └── useAdminTable.ts │ │ │ ├── c-metadata-component.ts │ │ │ ├── display │ │ │ │ ├── c-display.spec.tsx │ │ │ │ ├── c-display.vue │ │ │ │ ├── c-list-range-display.vue │ │ │ │ ├── c-loader-status.spec.tsx │ │ │ │ ├── c-loader-status.vue │ │ │ │ └── c-table.vue │ │ │ ├── index.ts │ │ │ └── input │ │ │ │ ├── c-datetime-picker.spec.tsx │ │ │ │ ├── c-datetime-picker.vue │ │ │ │ ├── c-input.spec.tsx │ │ │ │ ├── c-input.spec.vue │ │ │ │ ├── c-input.vue │ │ │ │ ├── c-list-filters.spec.tsx │ │ │ │ ├── c-list-filters.vue │ │ │ │ ├── c-list-page-size.vue │ │ │ │ ├── c-list-page.vue │ │ │ │ ├── c-list-pagination.vue │ │ │ │ ├── c-select-many-to-many.spec.tsx │ │ │ │ ├── c-select-many-to-many.vue │ │ │ │ ├── c-select-string-value.spec.tsx │ │ │ │ ├── c-select-string-value.vue │ │ │ │ ├── c-select-values.spec.tsx │ │ │ │ ├── c-select-values.vue │ │ │ │ ├── c-select.spec.tsx │ │ │ │ ├── c-select.vue │ │ │ │ └── c-time-picker.vue │ │ ├── composables │ │ │ └── useMetadata.ts │ │ ├── env.d.ts │ │ ├── index.ts │ │ ├── install.ts │ │ ├── shared.scss │ │ └── util.ts │ ├── test │ │ └── util.ts │ ├── tsconfig.build-types.json │ ├── tsconfig.build-utils.json │ ├── tsconfig.json │ └── vite.config.ts ├── coalesce-vue │ ├── .editorconfig │ ├── .gitignore │ ├── .vscode │ │ ├── launch.json │ │ └── settings.json │ ├── README.md │ ├── lib │ │ └── cjs │ │ │ └── package.json │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── api-client.ts │ │ ├── build.ts │ │ ├── index.ts │ │ ├── metadata.ts │ │ ├── model.ts │ │ ├── test-utils.ts │ │ ├── util.ts │ │ └── viewmodel.ts │ ├── test │ │ ├── api-client.spec.ts │ │ ├── global-setup.ts │ │ ├── model.display.spec.ts │ │ ├── model.shared.ts │ │ ├── model.toDto.spec.ts │ │ ├── model.toModel.spec.ts │ │ ├── targets.apiclients.ts │ │ ├── targets.metadata.ts │ │ ├── targets.models.ts │ │ ├── targets.viewmodels.ts │ │ ├── test-utils.spec.ts │ │ ├── test-utils.ts │ │ ├── utils.spec.ts │ │ ├── utils.spec.vue2.ts │ │ └── viewmodel.spec.ts │ ├── tsconfig.build.cjs.json │ ├── tsconfig.build.esm.json │ ├── tsconfig.json │ ├── vitest.config.ts │ └── vue3-tests │ │ ├── model.spec.vue3.ts │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── tsconfig.json │ │ └── vitest.config.ts └── test-targets │ ├── api-clients.g.ts │ ├── metadata.g.ts │ ├── models.g.ts │ └── viewmodels.g.ts └── templates └── Coalesce.Vue.Template ├── .gitignore ├── .vscode └── settings.json ├── Directory.Packages.props ├── IntelliTect.Coalesce.Vue.Template.csproj ├── README.md ├── TestLocal.ps1 ├── content ├── .editorconfig ├── .gitattributes ├── .github │ └── workflows │ │ └── build-test-and-deploy.yml ├── .gitignore ├── .template.config │ └── template.json ├── .vscode │ ├── extensions.json │ └── settings.json ├── Coalesce.Starter.Vue.Data.Test │ ├── Coalesce.Starter.Vue.Data.Test.csproj │ ├── UnitTest1.cs │ └── Utilities │ │ ├── AppDbContextForSqlite.cs │ │ ├── AssertionExtensions.cs │ │ ├── SqliteDatabaseFixture.cs │ │ └── TestBase.cs ├── Coalesce.Starter.Vue.Data │ ├── AppDbContext.cs │ ├── Auth │ │ ├── AppClaimTypes.cs │ │ ├── ClaimsPrincipalExtensions.cs │ │ ├── ClaimsPrincipalFactory.cs │ │ ├── DbContextFactoryExtensions.cs │ │ ├── InvitationService.cs │ │ ├── Permission.cs │ │ ├── SecurityService.cs │ │ ├── UserInfo.cs │ │ ├── UserInvitation.cs │ │ └── UserManagementService.cs │ ├── Coalesce.Starter.Vue.Data.csproj │ ├── Coalesce │ │ ├── AppBehaviors.cs │ │ ├── AppDataSource.cs │ │ └── AuditOperationContext.cs │ ├── Communication │ │ ├── AzureEmailOptions.cs │ │ ├── AzureEmailService.cs │ │ ├── IEmailService.cs │ │ ├── NoOpEmailService.cs │ │ ├── SendGridEmailOptions.cs │ │ └── SendGridEmailService.cs │ ├── DatabaseSeeder.cs │ ├── DevelopmentAppDbContextFactory.cs │ ├── GlobalUsings.cs │ ├── Models │ │ ├── AuditLog.cs │ │ ├── Role.cs │ │ ├── Tenancy │ │ │ ├── ITenanted.cs │ │ │ ├── Tenant.cs │ │ │ ├── TenantMembership.cs │ │ │ └── TenantedBase.cs │ │ ├── TrackingBase.cs │ │ ├── User.cs │ │ ├── UserPhoto.cs │ │ ├── UserRole.cs │ │ └── Widget.cs │ └── Utilities │ │ └── QueryableExtensions.cs ├── Coalesce.Starter.Vue.Web │ ├── .vscode │ │ ├── extensions.json │ │ └── settings.json │ ├── Api │ │ └── Generated │ │ │ ├── AuditLogController.g.cs │ │ │ ├── RoleController.g.cs │ │ │ ├── SecurityServiceController.g.cs │ │ │ ├── TenantController.g.cs │ │ │ ├── UserController.g.cs │ │ │ ├── UserRoleController.g.cs │ │ │ └── WidgetController.g.cs │ ├── AppInsightsTelemetryEnricher.cs │ ├── Coalesce.Starter.Vue.Web.csproj │ ├── Controllers │ │ └── HomeController.cs │ ├── Models │ │ └── Generated │ │ │ ├── AuditLogDto.g.cs │ │ │ ├── AuditLogPropertyDto.g.cs │ │ │ ├── RoleDto.g.cs │ │ │ ├── TenantDto.g.cs │ │ │ ├── UserDto.g.cs │ │ │ ├── UserInfoDto.g.cs │ │ │ ├── UserRoleDto.g.cs │ │ │ └── WidgetDto.g.cs │ ├── Pages │ │ ├── ConfirmEmail.cshtml │ │ ├── ConfirmEmail.cshtml.cs │ │ ├── CreateTenant.cshtml │ │ ├── CreateTenant.cshtml.cs │ │ ├── ExternalLogin.cshtml │ │ ├── ExternalLogin.cshtml.cs │ │ ├── ForgotPassword.cshtml │ │ ├── ForgotPassword.cshtml.cs │ │ ├── Invitation.cshtml │ │ ├── Invitation.cshtml.cs │ │ ├── Register.cshtml │ │ ├── Register.cshtml.cs │ │ ├── ResetPassword.cshtml │ │ ├── ResetPassword.cshtml.cs │ │ ├── SelectTenant.cshtml │ │ ├── SelectTenant.cshtml.cs │ │ ├── SignIn.cshtml │ │ ├── SignIn.cshtml.cs │ │ ├── SignOut.cshtml │ │ ├── SignOut.cshtml.cs │ │ ├── _Layout.cshtml │ │ ├── _ViewImports.cshtml │ │ └── _ViewStart.cshtml │ ├── Program.cs │ ├── ProgramAuthConfiguration.cs │ ├── Properties │ │ └── launchSettings.json │ ├── appsettings.json │ ├── eslint.config.mjs │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── google-logo.svg │ │ └── microsoft-logo.svg │ ├── src │ │ ├── App.vue │ │ ├── api-clients.g.ts │ │ ├── assets │ │ │ └── logo.png │ │ ├── components │ │ │ ├── HelloWorld.spec.ts │ │ │ ├── HelloWorld.vue │ │ │ └── display │ │ │ │ └── UserAvatar.vue │ │ ├── composables │ │ │ ├── useForm.ts │ │ │ ├── useTitle.ts │ │ │ └── useUser.ts │ │ ├── main.ts │ │ ├── metadata.g.ts │ │ ├── models.g.ts │ │ ├── router.ts │ │ ├── styles │ │ │ └── site.scss │ │ ├── test-utils.ts │ │ ├── types │ │ │ ├── auto-imports.d.ts │ │ │ └── env.d.ts │ │ ├── user-service.ts │ │ ├── viewmodels.g.ts │ │ └── views │ │ │ ├── Admin.vue │ │ │ ├── Home.vue │ │ │ ├── OpenAPI.vue │ │ │ ├── UserProfile.vue │ │ │ ├── WidgetEdit.spec.ts │ │ │ ├── WidgetEdit.vue │ │ │ └── errors │ │ │ ├── Forbidden.vue │ │ │ └── NotFound.vue │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── vite.config.ts │ └── web.config ├── Coalesce.Starter.Vue.sln ├── Directory.Build.props ├── azure-pipelines.yml └── coalesce.json └── license.txt /.editorconfig: -------------------------------------------------------------------------------- 1 | # Suppress: EC112 2 | 3 | [*.{vue,ts,js,scss,html,csproj,md,props,targets,yml}] 4 | indent_style = space 5 | indent_size = 2 6 | 7 | 8 | [*.cs] 9 | 10 | # RCS1036: Remove redundant empty line. 11 | dotnet_diagnostic.RCS1036.severity = none 12 | 13 | # RCS1037: Remove trailing white-space. 14 | dotnet_diagnostic.RCS1037.severity = none 15 | 16 | # RCS1160: Abstract type should not have public constructors. 17 | dotnet_diagnostic.RCS1160.severity = none 18 | 19 | # RS1024: Compare symbols correctly 20 | dotnet_diagnostic.RS1024.severity = suggestion 21 | 22 | # RCS1090: Call 'ConfigureAwait(false)'. 23 | dotnet_diagnostic.RCS1090.severity = none 24 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | 2 | *.g.cs linguist-generated=true 3 | *.g.ts linguist-generated=true 4 | *.generated.d.ts linguist-generated=true 5 | playground/**/Scripts/Coalesce/* linguist-generated=true 6 | playground/**/Scripts/Coalesce/**/* linguist-generated=true 7 | playground/**/Views/Generated/* linguist-generated=true 8 | playground/**/Views/Generated/**/* linguist-generated=true -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Main Workflows 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | groups: 9 | dev-dependencies: 10 | patterns: 11 | - "*-artifact" 12 | 13 | # Vue Template Project 14 | - package-ecosystem: "github-actions" 15 | directory: "/templates/Coalesce.Vue.Template/content/.github/workflows" 16 | schedule: 17 | interval: "weekly" 18 | groups: 19 | dev-dependencies: 20 | patterns: 21 | - "*-artifact" 22 | -------------------------------------------------------------------------------- /.github/workflows/alpha.yml: -------------------------------------------------------------------------------- 1 | name: build-alpha 2 | 3 | on: 4 | pull_request: 5 | branches: [dev, next] 6 | paths-ignore: ['docs/**'] 7 | push: 8 | branches: [dev, next] 9 | paths-ignore: ['docs/**'] 10 | workflow_dispatch: 11 | 12 | jobs: 13 | meta: 14 | uses: ./.github/workflows/part-compute-version.yml 15 | secrets: inherit 16 | with: 17 | prereleaseSlug: ci 18 | 19 | build: 20 | uses: ./.github/workflows/part-build.yml 21 | secrets: inherit 22 | needs: meta 23 | with: 24 | COALESCE_VERSION: ${{ needs.meta.outputs.COALESCE_VERSION }} 25 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dotnet.defaultSolution": "Coalesce.sln", 3 | 4 | "editor.formatOnPaste": false, 5 | "editor.formatOnSave": true, 6 | "editor.formatOnType": true, 7 | "editor.defaultFormatter": "esbenp.prettier-vscode" 8 | } 9 | -------------------------------------------------------------------------------- /Coalesce.lutconfig: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | 30000 6 | -------------------------------------------------------------------------------- /coalesce-vue2.json: -------------------------------------------------------------------------------- 1 | { 2 | "webProject": { 3 | "projectFile": "./playground/Coalesce.Web.Vue2/Coalesce.Web.Vue2.csproj", 4 | "framework": "net8.0" 5 | }, 6 | "dataProject": { 7 | "projectFile": "./playground/Coalesce.Domain/Coalesce.Domain.csproj", 8 | "framework": "net8.0" 9 | }, 10 | //"rootTypesWhitelist": [ 11 | // "CaseDto" 12 | //], 13 | "rootGenerator": "Vue" 14 | } -------------------------------------------------------------------------------- /coalesce-vue3.json: -------------------------------------------------------------------------------- 1 | { 2 | "webProject": { 3 | "projectFile": "./playground/Coalesce.Web.Vue3/Coalesce.Web.Vue3.csproj", 4 | "framework": "net9.0" 5 | }, 6 | "dataProject": { 7 | "projectFile": "./playground/Coalesce.Domain/Coalesce.Domain.csproj", 8 | "framework": "net9.0" 9 | }, 10 | //"rootTypesWhitelist": [ 11 | // "CaseDto" 12 | //], 13 | "rootGenerator": "Vue" 14 | } -------------------------------------------------------------------------------- /docs/.vitepress/linkcheck-skip-file.txt: -------------------------------------------------------------------------------- 1 | github.com/intellitect/coalesce/edit 2 | 3 | # fails on github actions randomly: 4 | https://www.npmjs.com/package 5 | https://github.com/IntelliTect/Coalesce 6 | https://intellitect.com 7 | https://www.intellitect.com 8 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | // https://vitepress.dev/guide/custom-theme 2 | import { h } from 'vue' 3 | import type { Theme } from 'vitepress' 4 | import DefaultTheme from 'vitepress/theme' 5 | import './style.scss' 6 | 7 | import CodeTabs from '../components/CodeTabs.vue'; 8 | import Prop from '../components/Prop.vue'; 9 | import SiteFooter from '../components/SiteFooter.vue'; 10 | 11 | export default { 12 | extends: DefaultTheme, 13 | Layout: () => { 14 | return h(DefaultTheme.Layout, null, { 15 | // https://vitepress.dev/guide/extending-default-theme#layout-slots 16 | 'doc-bottom': () => h(SiteFooter, {class: 'page-footer'}) 17 | }) 18 | }, 19 | enhanceApp({ app, router, siteData }) { 20 | // ... 21 | app.component("CodeTabs", CodeTabs) 22 | app.component("Prop", Prop) 23 | app.component("SiteFooter", SiteFooter) 24 | } 25 | } satisfies Theme 26 | -------------------------------------------------------------------------------- /docs/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.enabled": true, 3 | } -------------------------------------------------------------------------------- /docs/modeling/model-components/attributes/coalesce.md: -------------------------------------------------------------------------------- 1 | 2 | # [Coalesce] 3 | 4 | `IntelliTect.Coalesce.CoalesceAttribute` 5 | 6 | Used to mark a type or member for generation by Coalesce. 7 | 8 | Some types and members will be implicitly included in generation - for example, all types represented by a `DbSet` on a `DbContext` that has a `[Coalesce]` attribute will automatically be included. Properties on these types will also be generated for unless explicitly excluded, since this is by far the most common usage scenario in Coalesce. 9 | 10 | On the other hand, [Methods](/modeling/model-components/methods.md) on these types will not have endpoints generated unless they are explicitly annotated with `[Coalesce]` to avoid accidentally exposing methods that were perhaps not intended to be exposed. 11 | 12 | The documentation pages for types and members that require/accept this attribute will state as such. An exhaustive list of all valid targets for `[Coalesce]` will not be found on this page. 13 | -------------------------------------------------------------------------------- /docs/modeling/model-components/attributes/create-controller.md: -------------------------------------------------------------------------------- 1 | --- 2 | deprecated: true 3 | --- 4 | 5 | # [CreateController] 6 | 7 | `IntelliTect.Coalesce.DataAnnotations.CreateControllerAttribute` 8 | 9 | By default an API and View controller are both created. This allows for 10 | suppressing the creation of either or both of these. 11 | 12 | 13 | ## Example Usage 14 | 15 | ``` c# 16 | [CreateController(view: false, api: true)] 17 | public class Person 18 | { 19 | public int PersonId { get; set; } 20 | 21 | ... 22 | } 23 | ``` 24 | 25 | ## Properties 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /docs/modeling/model-components/attributes/inject.md: -------------------------------------------------------------------------------- 1 | 2 | # [Inject] 3 | 4 | `IntelliTect.Coalesce.DataAnnotations.InjectAttribute` 5 | 6 | Used to mark a [Method](/modeling/model-components/methods.md) parameter for dependency injection from the application's `IServiceProvider`. 7 | 8 | See [Methods](/modeling/model-components/methods.md) for more. 9 | 10 | This gets translated to a `Microsoft.AspNetCore.Mvc.FromServicesAttribute` in the generated API controller's action. 11 | 12 | 13 | ## Example Usage 14 | 15 | ``` c# 16 | public class Person 17 | { 18 | public int PersonId { get; set; } 19 | public string FirstName { get; set; } 20 | public string LastName { get; set; } 21 | 22 | public string GetFullName([Inject] ILogger logger) 23 | { 24 | logger.LogInformation("Person " + PersonId + "'s full name was requested"); 25 | return FirstName + " " + LastName; 26 | } 27 | } 28 | ``` 29 | -------------------------------------------------------------------------------- /docs/modeling/model-components/attributes/list-text.md: -------------------------------------------------------------------------------- 1 | 2 | # [ListText] 3 | 4 | `IntelliTect.Coalesce.DataAnnotations.ListTextAttribute` 5 | 6 | When a textual representation of an object needs to be displayed in the UI, this attribute controls which property will be used. Examples include dropdowns and cells in admin UI tables. 7 | 8 | If this attribute is not used, and a property named `Name` exists on the model, that property will be used. Otherwise, the primary key will be used. 9 | 10 | 11 | ## Example Usage 12 | 13 | ``` c# 14 | public class Person 15 | { 16 | public int PersonId { get; set; } 17 | 18 | public string FirstName { get; set; } 19 | 20 | public string LastName { get; set; } 21 | 22 | [ListText] 23 | [NotMapped] 24 | public string Name => FirstName + " " + LastName 25 | } 26 | ``` 27 | -------------------------------------------------------------------------------- /docs/modeling/model-components/attributes/load-from-data-source.md: -------------------------------------------------------------------------------- 1 | --- 2 | deprecated: true 3 | --- 4 | 5 | # [LoadFromDataSource] 6 | 7 | `IntelliTect.Coalesce.DataAnnotations.LoadFromDataSourceAttribute` 8 | 9 | Specifies that the targeted model instance method should load the instance it is called on from the specified data source when invoked from an API endpoint. If not defined, the model's default data source is used. 10 | 11 | ## Example Usage 12 | 13 | ``` c# 14 | public class Person 15 | { 16 | public int PersonId { get; set; } 17 | public string FirstName { get; set; } 18 | 19 | [Coalesce, LoadFromDataSource(typeof(WithoutCases))] 20 | public void ChangeSpacesToDashesInName() 21 | { 22 | FirstName = FirstName.Replace(" ", "-"); 23 | } 24 | } 25 | ``` 26 | 27 | ## Properties 28 | 29 | 30 | 31 | The name of the [Data Source](/modeling/model-components/data-sources.md) to load the instance object from. 32 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "coalesce-docs", 3 | "version": "1.0.0", 4 | "description": "", 5 | "private": true, 6 | "repository": "https://github.com/IntelliTect/Coalesce", 7 | "author": "IntelliTect", 8 | "license": "Apache-2.0", 9 | "scripts": { 10 | "dev": "vitepress dev", 11 | "linkcheck": "linkcheck localhost:8087/Coalesce -e --skip-file ./.vitepress/linkcheck-skip-file.txt", 12 | "build": "cspell \"**/*.md\" && vitepress build --outDir .vitepress/dist/Coalesce && start-server-and-test 'serve -Ll 8087 ./.vitepress/dist' 8087 linkcheck" 13 | }, 14 | "dependencies": { 15 | "shiki": "1.4.0" 16 | }, 17 | "devDependencies": { 18 | "cspell": "^5.20.0", 19 | "glob": "^8.0.1", 20 | "gray-matter": "^4.0.3", 21 | "linkcheck-bin": "3.0.0-0", 22 | "sass": "^1.69.5", 23 | "serve": "^14.2.3", 24 | "start-server-and-test": "^2.0.3", 25 | "vitepress": "1.1.4" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /docs/public/ef-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelliTect/Coalesce/bbbb55fd51a58355ab296730e7b0f40f4f543ca6/docs/public/favicon.ico -------------------------------------------------------------------------------- /docs/public/net-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/public/vue-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /docs/stacks/vue/coalesce-vue-vuetify/components/c-admin-editor.md: -------------------------------------------------------------------------------- 1 | # c-admin-editor 2 | 3 | 4 | 5 | An editor for a single [ViewModel](/stacks/vue/layers/viewmodels.md) instance. Provides a [c-input](/stacks/vue/coalesce-vue-vuetify/components/c-input.md) for each property of the model. 6 | 7 | 8 | 9 | Does not automatically enable [auto-save](/stacks/vue/layers/viewmodels.md) - if desired, this must be enabled by the implementor of this component. 10 | 11 | ## Examples 12 | 13 | ``` vue-html 14 | 15 | ``` 16 | 17 | ## Props 18 | 19 | 20 | 21 | The [ViewModel](/stacks/vue/layers/viewmodels.md) to render an editor for. 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /docs/stacks/vue/coalesce-vue-vuetify/components/c-list-filters.md: -------------------------------------------------------------------------------- 1 | # c-list-filters 2 | 3 | 4 | 5 | A component that provides an interface for modifying the `filters` prop of a [ListViewModel](/stacks/vue/layers/viewmodels.md)'s [parameters](/modeling/model-components/data-sources.md#standard-parameters). 6 | 7 | 8 | 9 | ## Example Usage 10 | 11 | ``` vue-html 12 | 13 | ``` 14 | 15 | ## Props 16 | 17 | 18 | 19 | The [ListViewModel](/stacks/vue/layers/viewmodels.md) whose filters will be editable. 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /docs/stacks/vue/coalesce-vue-vuetify/components/c-list-page-size.md: -------------------------------------------------------------------------------- 1 | # c-list-page-size 2 | 3 | 4 | 5 | A component that provides an dropdown for modifying the `pageSize` [parameter](/modeling/model-components/data-sources.md#standard-parameters) prop of a [ListViewModel](/stacks/vue/layers/viewmodels.md). 6 | 7 | 8 | 9 | ## Example Usage 10 | 11 | ``` vue-html 12 | 13 | ``` 14 | 15 | ## Props 16 | 17 | 18 | 19 | The [ListViewModel](/stacks/vue/layers/viewmodels.md) whose pagination will be editable. 20 | 21 | 22 | 23 | An optional list of available page sizes to offer through [c-list-page-size](/stacks/vue/coalesce-vue-vuetify/components/c-list-page-size.md). Defaults to `[10, 25, 100]`. 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/stacks/vue/coalesce-vue-vuetify/components/c-list-page.md: -------------------------------------------------------------------------------- 1 | # c-list-page 2 | 3 | 4 | 5 | A component that provides previous/next buttons and a text field for modifying the `page` [parameter](/modeling/model-components/data-sources.md#standard-parameters) prop of a [ListViewModel](/stacks/vue/layers/viewmodels.md). 6 | 7 | 8 | 9 | ## Example Usage 10 | 11 | ``` vue-html 12 | 13 | ``` 14 | 15 | ## Props 16 | 17 | 18 | 19 | The [ListViewModel](/stacks/vue/layers/viewmodels.md) whose current page will be changeable with the component. 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /docs/stacks/vue/coalesce-vue-vuetify/components/c-list-range-display.md: -------------------------------------------------------------------------------- 1 | # c-list-range-display 2 | 3 | 4 | 5 | Displays pagination information about the current `$items` of a [ListViewModel](/stacks/vue/layers/viewmodels.md) in the format `` - of ``. 6 | 7 | 8 | 9 | Uses the pagination information returned from the last successful `$load` call, not the current `$params` of the [ListViewModel](/stacks/vue/layers/viewmodels.md). 10 | 11 | ## Examples 12 | 13 | ``` vue-html 14 | 15 | ``` 16 | 17 | ## Props 18 | 19 | 20 | 21 | The [ListViewModel](/stacks/vue/layers/viewmodels.md) to display pagination information for. 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /docs/topics/coalesce-swashbuckle-with.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelliTect/Coalesce/bbbb55fd51a58355ab296730e7b0f40f4f543ca6/docs/topics/coalesce-swashbuckle-with.jpg -------------------------------------------------------------------------------- /docs/topics/coalesce-swashbuckle-without.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelliTect/Coalesce/bbbb55fd51a58355ab296730e7b0f40f4f543ca6/docs/topics/coalesce-swashbuckle-without.jpg -------------------------------------------------------------------------------- /docs/topics/security-overview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelliTect/Coalesce/bbbb55fd51a58355ab296730e7b0f40f4f543ca6/docs/topics/security-overview.jpg -------------------------------------------------------------------------------- /playground/Coalesce.Domain/CaseDtoStandalone.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using IntelliTect.Coalesce; 3 | 4 | namespace Coalesce.Domain 5 | { 6 | [Coalesce] 7 | public class CaseDtoStandalone : IClassDto 8 | { 9 | [Key] 10 | public int CaseId { get; set; } 11 | 12 | public string? Title { get; set; } 13 | 14 | public void MapTo(Case obj, IMappingContext context) 15 | { 16 | obj.Title = Title; 17 | } 18 | 19 | public void MapFrom(Case obj, IMappingContext context, IncludeTree? tree = null) 20 | { 21 | CaseId = obj.CaseKey; 22 | Title = obj.Title; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /playground/Coalesce.Domain/CaseProduct.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace Coalesce.Domain 5 | { 6 | [Table("CaseProduct")] 7 | public class CaseProduct 8 | { 9 | public int CaseProductId { get; set; } 10 | public int CaseId { get; set; } 11 | 12 | [Search] 13 | public Case Case { get; set; } = null!; 14 | 15 | public int ProductId { get; set; } 16 | 17 | [Search] 18 | public Product Product { get; set; } = null!; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /playground/Coalesce.Domain/CaseStandalone.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce; 2 | using System.Linq; 3 | 4 | namespace Coalesce.Domain 5 | { 6 | [Coalesce, StandaloneEntity] 7 | public class CaseStandalone 8 | { 9 | public int Id { get; private set; } 10 | 11 | public Person? AssignedTo { get; private set; } 12 | 13 | [DefaultDataSource] 14 | public class DefaultSource(CrudContext context) : StandardDataSource(context) 15 | { 16 | public override IQueryable GetQuery(IDataSourceParameters parameters) 17 | { 18 | return Db.Cases 19 | .Select(c => new CaseStandalone 20 | { 21 | Id = c.CaseKey, 22 | AssignedTo = c.AssignedTo 23 | }); 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /playground/Coalesce.Domain/CaseSummary.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce; 2 | using IntelliTect.Coalesce.DataAnnotations; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace Coalesce.Domain 9 | { 10 | public class CaseSummary 11 | { 12 | public int CaseSummaryId { get; set; } 13 | public int OpenCases { get; set; } 14 | public int CaseCount { get; set; } 15 | public int CloseCases { get; set; } 16 | 17 | [ListText] 18 | public string? Description { get; set; } 19 | 20 | public IDictionary TestDict { get; set; } = new Dictionary(); 21 | 22 | [Coalesce] 23 | public static CaseSummary GetCaseSummary(AppDbContext db) 24 | { 25 | return new CaseSummary() 26 | { 27 | CaseSummaryId = 1, 28 | OpenCases = 2, 29 | CaseCount = db.Cases.Count(), 30 | CloseCases = 4, 31 | Description = "This is a test!" 32 | }; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /playground/Coalesce.Domain/Coalesce.Domain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | net8.0;net9.0 6 | false 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /playground/Coalesce.Domain/External/DevTeam.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace Coalesce.Domain.External 7 | { 8 | public class DevTeam 9 | { 10 | public int DevTeamId { get; set; } 11 | public string? Name { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /playground/Coalesce.Domain/Log.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.DataAnnotations; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Coalesce.Domain 7 | { 8 | [Edit(PermissionLevel = SecurityPermissionLevels.DenyAll)] 9 | [Create(PermissionLevel = SecurityPermissionLevels.DenyAll)] 10 | [Delete(PermissionLevel = SecurityPermissionLevels.DenyAll)] 11 | public class Log 12 | { 13 | #nullable disable 14 | public int LogId { get; set; } 15 | public string Level { get; set; } 16 | public string Message { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /playground/Coalesce.Domain/Migrations/20160811224626_MakePersonStatsIdNullable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.EntityFrameworkCore.Migrations; 4 | 5 | namespace Coalesce.Domain.Migrations 6 | { 7 | public partial class MakePersonStatsIdNullable : Migration 8 | { 9 | protected override void Up(MigrationBuilder migrationBuilder) 10 | { 11 | migrationBuilder.AlterColumn( 12 | name: "PersonStatsId", 13 | table: "Person", 14 | nullable: true); 15 | } 16 | 17 | protected override void Down(MigrationBuilder migrationBuilder) 18 | { 19 | migrationBuilder.AlterColumn( 20 | name: "PersonStatsId", 21 | table: "Person", 22 | nullable: false); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /playground/Coalesce.Domain/Migrations/20180116002515_AddTimeSpan.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace Coalesce.Domain.Migrations 6 | { 7 | public partial class AddTimeSpan : Migration 8 | { 9 | protected override void Up(MigrationBuilder migrationBuilder) 10 | { 11 | migrationBuilder.DropColumn( 12 | name: "PersonStatsId", 13 | table: "Person"); 14 | 15 | migrationBuilder.AddColumn( 16 | name: "Duration", 17 | table: "Case", 18 | nullable: false, 19 | defaultValue: new TimeSpan(0, 0, 0, 0, 0)); 20 | } 21 | 22 | protected override void Down(MigrationBuilder migrationBuilder) 23 | { 24 | migrationBuilder.DropColumn( 25 | name: "Duration", 26 | table: "Case"); 27 | 28 | migrationBuilder.AddColumn( 29 | name: "PersonStatsId", 30 | table: "Person", 31 | nullable: true); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /playground/Coalesce.Domain/Migrations/20180205233522_AddCompanyIsDeletedFlag.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace Coalesce.Domain.Migrations 6 | { 7 | public partial class AddCompanyIsDeletedFlag : Migration 8 | { 9 | protected override void Up(MigrationBuilder migrationBuilder) 10 | { 11 | migrationBuilder.AddColumn( 12 | name: "IsDeleted", 13 | table: "Company", 14 | nullable: false, 15 | defaultValue: false); 16 | } 17 | 18 | protected override void Down(MigrationBuilder migrationBuilder) 19 | { 20 | migrationBuilder.DropColumn( 21 | name: "IsDeleted", 22 | table: "Company"); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /playground/Coalesce.Domain/Migrations/20180530231723_FixUniqueIdNameCollisionWithSqlType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | namespace Coalesce.Domain.Migrations 5 | { 6 | public partial class FixUniqueIdNameCollisionWithSqlType : Migration 7 | { 8 | protected override void Up(MigrationBuilder migrationBuilder) 9 | { 10 | migrationBuilder.AddColumn( 11 | name: "ProductUniqueId", 12 | table: "Product", 13 | nullable: false, 14 | defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); 15 | } 16 | 17 | protected override void Down(MigrationBuilder migrationBuilder) 18 | { 19 | migrationBuilder.DropColumn( 20 | name: "ProductUniqueId", 21 | table: "Product"); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /playground/Coalesce.Domain/Migrations/20200714190517_AddZipCode.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | namespace Coalesce.Domain.Migrations 4 | { 5 | public partial class AddZipCode : Migration 6 | { 7 | protected override void Up(MigrationBuilder migrationBuilder) 8 | { 9 | migrationBuilder.CreateTable( 10 | name: "ZipCodes", 11 | columns: table => new 12 | { 13 | Zip = table.Column(nullable: false), 14 | State = table.Column(nullable: true) 15 | }, 16 | constraints: table => 17 | { 18 | table.PrimaryKey("PK_ZipCodes", x => x.Zip); 19 | }); 20 | } 21 | 22 | protected override void Down(MigrationBuilder migrationBuilder) 23 | { 24 | migrationBuilder.DropTable( 25 | name: "ZipCodes"); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /playground/Coalesce.Domain/Migrations/20231114001120_AddAuditLogDescription.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace Coalesce.Domain.Migrations 6 | { 7 | public partial class AddAuditLogDescription : Migration 8 | { 9 | protected override void Up(MigrationBuilder migrationBuilder) 10 | { 11 | migrationBuilder.AddColumn( 12 | name: "Description", 13 | table: "AuditLogs", 14 | type: "nvarchar(max)", 15 | nullable: true); 16 | } 17 | 18 | protected override void Down(MigrationBuilder migrationBuilder) 19 | { 20 | migrationBuilder.DropColumn( 21 | name: "Description", 22 | table: "AuditLogs"); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /playground/Coalesce.Domain/Migrations/20240122232554_AddCollectionOfStrings.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace Coalesce.Domain.Migrations 6 | { 7 | /// 8 | public partial class AddCollectionOfStrings : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "ArbitraryCollectionOfStrings", 15 | table: "Person", 16 | type: "nvarchar(max)", 17 | nullable: true); 18 | } 19 | 20 | /// 21 | protected override void Down(MigrationBuilder migrationBuilder) 22 | { 23 | migrationBuilder.DropColumn( 24 | name: "ArbitraryCollectionOfStrings", 25 | table: "Person"); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /playground/Coalesce.Domain/Migrations/20241007205723_AddUniqueIndex.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace Coalesce.Domain.Migrations 6 | { 7 | /// 8 | public partial class AddUniqueIndex : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.Sql("UPDATE Product SET ProductUniqueId = NEWID()"); 14 | 15 | migrationBuilder.CreateIndex( 16 | name: "IX_Product_ProductUniqueId", 17 | table: "Product", 18 | column: "ProductUniqueId", 19 | unique: true); 20 | } 21 | 22 | /// 23 | protected override void Down(MigrationBuilder migrationBuilder) 24 | { 25 | migrationBuilder.DropIndex( 26 | name: "IX_Product_ProductUniqueId", 27 | table: "Product"); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /playground/Coalesce.Domain/PersonLocation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Coalesce.Domain 8 | { 9 | [Table("PersonLocation")] 10 | public class PersonLocation 11 | { 12 | public double Latitude { get; set; } 13 | public double Longitude { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /playground/Coalesce.Domain/PersonStats.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Coalesce.Domain 8 | { 9 | [NotMapped] 10 | public class PersonStats 11 | { 12 | #nullable disable 13 | //public int PersonStatsId { get; set; } 14 | public double Height { get; set; } 15 | public double Weight { get; set; } 16 | public string Name { get; set; } 17 | 18 | [NotMapped] 19 | public ICollection NullableValueTypeCollection { get; set; } 20 | 21 | public ICollection ValueTypeCollection { get; set; } 22 | 23 | public PersonLocation PersonLocation { get; set; } 24 | } 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /playground/Coalesce.Domain/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace Coalesce.Domain 7 | { 8 | // *** Workaround for dotnet ef ... won't currently work on class libraries *** 9 | // http://benjii.me/2016/06/entity-framework-core-migrations-for-class-library-projects/ 10 | public static class Program 11 | { 12 | public static void Main(string[] args) { } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /playground/Coalesce.Domain/TemporaryDbContextFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Design; 3 | using Microsoft.EntityFrameworkCore.Infrastructure; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace Coalesce.Domain 10 | { 11 | public class TemporaryDbContextFactory : IDesignTimeDbContextFactory 12 | { 13 | public AppDbContext CreateDbContext(string[] args) 14 | { 15 | var builder = new DbContextOptionsBuilder(); 16 | builder.UseSqlServer("Server=(localdb)\\MSSQLLocalDB;Database=CoalesceDb;Trusted_Connection=True;"); 17 | return new AppDbContext(builder.Options); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /playground/Coalesce.Domain/ZipCode.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.DataAnnotations; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel.DataAnnotations; 5 | using System.ComponentModel.DataAnnotations.Schema; 6 | using System.Text; 7 | 8 | namespace Coalesce.Domain 9 | { 10 | public class ZipCode 11 | { 12 | [Key] 13 | [DatabaseGenerated(DatabaseGeneratedOption.None)] 14 | [ListText] 15 | public string Zip { get; set; } = null!; 16 | 17 | public string State { get; set; } = null!; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue2/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules\\typescript\\lib" 3 | } -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue2/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Error"; 3 | } 4 | 5 |

Error.

6 |

An error occurred while processing your request.

7 | 8 | @if (!string.IsNullOrEmpty((string?)ViewData["RequestId"])) 9 | { 10 |

11 | Request ID: @ViewData["RequestId"] 12 |

13 | } 14 | 15 |

Development Mode

16 |

17 | Swapping to Development environment will display more detailed information about the error that occurred. 18 |

19 |

20 | Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. 21 |

22 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue2/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @ViewData["Title"] - VuePlayground 7 | 8 | 9 | 10 | 11 | @RenderBody() 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue2/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Coalesce.Web.Vue 2 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 3 | @addTagHelper *, Microsoft.AspNetCore.SpaServices 4 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue2/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue2/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "Debug": { 5 | "LogLevel": { 6 | "Default": "Debug", 7 | "System": "Information", 8 | "Microsoft": "Information" 9 | } 10 | }, 11 | "Console": { 12 | "LogLevel": { 13 | "Default": "Debug", 14 | "System": "Information", 15 | "Microsoft": "Information" 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue2/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=CoalesceDb;Trusted_Connection=True;" 4 | }, 5 | 6 | "Logging": { 7 | "IncludeScopes": false, 8 | "Debug": { 9 | "LogLevel": { 10 | "Default": "Warning" 11 | } 12 | }, 13 | "Console": { 14 | "LogLevel": { 15 | "Default": "Warning" 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | MyAppName 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue2/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelliTect/Coalesce/bbbb55fd51a58355ab296730e7b0f40f4f543ca6/playground/Coalesce.Web.Vue2/public/favicon.ico -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue2/src/css/site.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | .v-input--is-readonly { 4 | fieldset { 5 | border-style: dashed !important; 6 | } 7 | 8 | .v-input--switch__track { 9 | background: #e6e6e6; 10 | border: 1px dashed black; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue2/src/vue-shim.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.vue" { 2 | import VueConstructor from "vue"; 3 | export default VueConstructor; 4 | } -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue2/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "warning", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "rules": { 7 | "quotemark": [true, "single"], 8 | "indent": [true, "spaces", 2], 9 | "interface-name": false, 10 | "ordered-imports": false, 11 | "object-literal-sort-keys": false, 12 | "no-consecutive-blank-lines": false 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue3/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | extends: [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended', 9 | '@vue/eslint-config-typescript', 10 | ], 11 | rules: { 12 | 'vue/multi-word-component-names': 'off', 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue3/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnPaste": false, 3 | "editor.formatOnSave": true, 4 | "editor.formatOnType": true, 5 | "editor.defaultFormatter": "esbenp.prettier-vscode", 6 | "typescript.tsdk": "node_modules\\typescript\\lib", 7 | "dotnet.defaultSolution": "Coalesce.Web.Vue3.sln" 8 | } -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue3/Coalesce.Web.Vue3.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | InProcess 6 | 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue3/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Error"; 3 | } 4 | 5 |

Error.

6 |

An error occurred while processing your request.

7 | 8 | @if (!string.IsNullOrEmpty((string?)ViewData["RequestId"])) 9 | { 10 |

11 | Request ID: @ViewData["RequestId"] 12 |

13 | } 14 | 15 |

Development Mode

16 |

17 | Swapping to Development environment will display more detailed information about the error that occurred. 18 |

19 |

20 | Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. 21 |

22 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue3/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @ViewData["Title"] - VuePlayground 7 | 8 | 9 | 10 | 11 | @RenderBody() 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue3/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Coalesce.Web.Vue 2 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 3 | @addTagHelper *, Microsoft.AspNetCore.SpaServices 4 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue3/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue3/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "Debug": { 5 | "LogLevel": { 6 | "Default": "Debug", 7 | "System": "Information", 8 | "Microsoft": "Information" 9 | } 10 | }, 11 | "Console": { 12 | "LogLevel": { 13 | "Default": "Debug", 14 | "System": "Information", 15 | "Microsoft": "Information" 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue3/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=CoalesceDb;Trusted_Connection=True;" 4 | }, 5 | 6 | "Logging": { 7 | "IncludeScopes": false, 8 | "Debug": { 9 | "LogLevel": { 10 | "Default": "Warning" 11 | } 12 | }, 13 | "Console": { 14 | "LogLevel": { 15 | "Default": "Warning" 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Vuetify 3 Vite Preview 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue3/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelliTect/Coalesce/bbbb55fd51a58355ab296730e7b0f40f4f543ca6/playground/Coalesce.Web.Vue3/public/favicon.ico -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue3/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | Artboard 46 2 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue3/src/css/site.scss: -------------------------------------------------------------------------------- 1 | 2 | @media (max-width: 767px) { 3 | /* On small screens, the nav menu spans the full width of the screen. Leave a space for it. */ 4 | body { 5 | padding-top: 50px; 6 | } 7 | } -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue3/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | declare module "*.vue" { 5 | import type { DefineComponent } from "vue"; 6 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 7 | const component: DefineComponent<{}, {}, any>; 8 | export default component; 9 | } 10 | 11 | // declare module 'vuetify' 12 | // declare module 'vuetify/lib/components' 13 | // declare module 'vuetify/lib/directives' 14 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue3/src/examples/c-select-many-to-many/styling.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 29 | -------------------------------------------------------------------------------- /playground/Coalesce.Web.Vue3/src/worker.ts: -------------------------------------------------------------------------------- 1 | console.log('hello from worker'); 2 | -------------------------------------------------------------------------------- /playground/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12.0 4 | false 5 | true 6 | 7 | 8 | false 9 | 10 | 11 | 12 | 13 | all 14 | analyzers 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Cli.Common.props: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | .NET Core command-line tooling for IntelliTect.Coalesce code generation 8 | Exe 9 | 10 | 11 | LatestMinor 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Common.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0;net9.0 4 | 5 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.AuditLogging.Tests/Usings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.AuditLogging/DefaultAuditOperationContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.EntityFrameworkCore.ChangeTracking; 3 | using System.Security.Claims; 4 | 5 | namespace IntelliTect.Coalesce.AuditLogging; 6 | 7 | public class DefaultAuditOperationContext : IAuditOperationContext 8 | where TAuditLog : DefaultAuditLog 9 | { 10 | public DefaultAuditOperationContext(IHttpContextAccessor httpContextAccessor) 11 | { 12 | HttpContextAccessor = httpContextAccessor; 13 | } 14 | 15 | private IHttpContextAccessor HttpContextAccessor { get; } 16 | 17 | public HttpContext? HttpContext => HttpContextAccessor.HttpContext; 18 | 19 | public ClaimsPrincipal? User => HttpContext?.User; 20 | 21 | public virtual void Populate(TAuditLog auditEntry, EntityEntry changedEntity) 22 | { 23 | // read only once from the underlying AsyncLocal for perf: 24 | var context = HttpContext; 25 | 26 | auditEntry.ClientIp = context?.Connection?.RemoteIpAddress?.ToString(); 27 | auditEntry.Referrer = context?.Request?.GetTypedHeaders()?.Referer?.PathAndQuery; 28 | auditEntry.Endpoint = context?.Request?.Path; 29 | } 30 | } -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.AuditLogging/IAuditLogDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace IntelliTect.Coalesce.AuditLogging; 4 | 5 | /// 6 | /// An interface representing the s that hold the data produced by Coalesce's audit logging features. 7 | /// 8 | /// The type of entity representing a change to an entity. You are expected to make your own implementation of this entity in your application code, inheriting from or . 9 | /// 10 | public interface IAuditLogDbContext 11 | where TAuditLog : class, IAuditLog 12 | { 13 | DbSet AuditLogs { get; } 14 | 15 | DbSet AuditLogProperties { get; } 16 | 17 | /// 18 | /// When , audit operations on the will be skipped. 19 | /// 20 | bool SuppressAudit => false; 21 | } 22 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.AuditLogging/IAuditOperationContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.ChangeTracking; 2 | 3 | namespace IntelliTect.Coalesce.AuditLogging; 4 | 5 | /// 6 | /// Defines a service that can populate additional fields on the given before it is persisted to the database. Declare the implementation of this service by calling . 7 | /// 8 | public interface IAuditOperationContext : IAuditOperationContext 9 | { 10 | /// 11 | void Populate(TAuditLog auditEntry, EntityEntry changedEntity); 12 | 13 | void IAuditOperationContext.Populate(IAuditLog auditEntry, EntityEntry changedEntity) 14 | { 15 | Populate((TAuditLog) auditEntry, changedEntity); 16 | } 17 | } 18 | 19 | public interface IAuditOperationContext 20 | { 21 | /// 22 | /// A hook that may be overridden to populate additional contextual information on an audit entry. 23 | /// 24 | void Populate(IAuditLog auditEntry, EntityEntry changedEntity); 25 | } 26 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.AuditLogging/IntelliTect.Coalesce.AuditLogging.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | enable 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.AuditLogging/Models/AuditEntry.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.AuditLogging.Internal; 2 | using Microsoft.EntityFrameworkCore.ChangeTracking; 3 | using System.Collections.Generic; 4 | 5 | namespace IntelliTect.Coalesce.AuditLogging; 6 | 7 | public class AuditEntry 8 | { 9 | public required EntityEntry Entry { get; init; } 10 | 11 | public required CoalesceAudit Parent { get; init; } 12 | 13 | public required AuditEntryState State { get; set; } 14 | 15 | public List Properties { get; set; } = new(); 16 | 17 | public object Entity => Entry.Entity; 18 | } 19 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.AuditLogging/Models/AuditEntryProperty.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.ChangeTracking; 2 | 3 | namespace IntelliTect.Coalesce.AuditLogging; 4 | 5 | public class AuditEntryProperty 6 | { 7 | public required PropertyEntry PropertyEntry { get; init; } 8 | 9 | public required AuditEntry Parent { get; init; } 10 | 11 | public string PropertyName => PropertyEntry.Metadata.Name; 12 | 13 | public bool IsKey => PropertyEntry.Metadata.IsKey(); 14 | 15 | public object? NewValue { get; set; } 16 | public string? NewValueDescription { get; set; } 17 | 18 | public string? NewValueFormatted => Parent.State == AuditEntryState.EntityDeleted 19 | ? null 20 | : Parent.Parent.Configuration.GetFormattedValue(PropertyEntry, NewValue); 21 | 22 | public object? OldValue { get; set; } 23 | public string? OldValueDescription { get; set; } 24 | 25 | public string? OldValueFormatted => Parent.State == AuditEntryState.EntityAdded 26 | ? null 27 | : Parent.Parent.Configuration.GetFormattedValue(PropertyEntry, OldValue); 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.AuditLogging/Models/AuditEntryState.cs: -------------------------------------------------------------------------------- 1 | namespace IntelliTect.Coalesce.AuditLogging; 2 | 3 | /// 4 | /// Represents the kind of operation that was performed on an entity. 5 | /// 6 | public enum AuditEntryState : byte 7 | { 8 | /// 9 | /// The entity was added 10 | /// 11 | EntityAdded = 0, 12 | 13 | /// 14 | /// The entity was deleted 15 | /// 16 | EntityDeleted = 1, 17 | 18 | /// 19 | /// The entity was modified 20 | /// 21 | EntityModified = 2 22 | } 23 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.CodeGeneration.Api/IntelliTect.Coalesce.CodeGeneration.Api.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Shared API code generation for IntelliTect.Coalesce 5 | AnyCPU 6 | Library 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.CodeGeneration.Api/MvcCodeGenerationExtensions.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.DataAnnotations; 2 | using IntelliTect.Coalesce.TypeDefinition; 3 | using IntelliTect.Coalesce.Utilities; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | namespace IntelliTect.Coalesce.CodeGeneration.Api 10 | { 11 | public static class MvcCodeGenerationExtensions 12 | { 13 | public static string MvcAnnotation(this SecurityPermission permission) 14 | { 15 | if (permission.NoAccess) throw new InvalidOperationException($"Cannot emit an annotation for security level {SecurityPermissionLevels.DenyAll}"); 16 | if (permission.AllowAnonymous) return "[AllowAnonymous]"; 17 | if (permission.HasRoles) return string.Concat(permission.RoleLists.Select(rl => 18 | $"[Authorize(Roles={string.Join(",", rl).QuotedStringLiteralForCSharp()})]")); 19 | return "[Authorize]"; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.CodeGeneration.Api/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:27477/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "IntelliTect.Coalesce.CodeGeneration.Api": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "http://localhost:27478/" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.CodeGeneration.Tests/.gitignore: -------------------------------------------------------------------------------- 1 | out -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.CodeGeneration.Vue/Generators/Controllers.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.CodeGeneration.Api.BaseGenerators; 2 | using IntelliTect.Coalesce.CodeGeneration.Api.Generators; 3 | using IntelliTect.Coalesce.CodeGeneration.Generation; 4 | using IntelliTect.Coalesce.TypeDefinition; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Text; 10 | 11 | namespace IntelliTect.Coalesce.CodeGeneration.Vue.Generators 12 | { 13 | public class Controllers : IntelliTect.Coalesce.CodeGeneration.Api.Generators.Controllers 14 | { 15 | public Controllers(CompositeGeneratorServices services) : base(services) { } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.CodeGeneration.Vue/IntelliTect.Coalesce.CodeGeneration.Vue.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vue.js code generation for IntelliTect.Coalesce 5 | AnyCPU 6 | Library 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.CodeGeneration.Vue/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:28570/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "IntelliTect.Coalesce.CodeGeneration.Vue": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "http://localhost:28571/" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.CodeGeneration/Analysis/Base/IProjectContextFactory.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.CodeGeneration.Configuration; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace IntelliTect.Coalesce.CodeGeneration.Analysis.Base 8 | { 9 | public interface IProjectContextFactory 10 | { 11 | ProjectContext CreateContext(ProjectConfiguration projectConfig, bool restore = false); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.CodeGeneration/Analysis/Base/TypeLocator.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.TypeDefinition; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace IntelliTect.Coalesce.CodeGeneration.Analysis.Base 8 | { 9 | public abstract class TypeLocator 10 | { 11 | public abstract TypeViewModel FindType(string typeName, bool throwWhenNotFound = true); 12 | 13 | public abstract IEnumerable FindDerivedTypes(string typeName, bool throwWhenNotFound = true); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.CodeGeneration/Analysis/MsBuild/Targets/Imports.targets: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | $(NoWarn);NU1603 10 | 11 | 12 | 13 | 15 | 16 | 17 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.CodeGeneration/Analysis/ProjectAnalysisException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.Serialization; 4 | using System.Text; 5 | 6 | namespace IntelliTect.Coalesce.CodeGeneration.Analysis 7 | { 8 | #pragma warning disable RCS1194 // Implement exception constructors. 9 | public class ProjectAnalysisException : Exception 10 | #pragma warning restore RCS1194 // Implement exception constructors. 11 | { 12 | public ProjectAnalysisException() 13 | { 14 | } 15 | 16 | public ProjectAnalysisException(string message, ICollection outputLines) : base(message) 17 | { 18 | OutputLines = outputLines; 19 | } 20 | 21 | public ICollection OutputLines { get; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.CodeGeneration/Generation/Cleaners/ICleaner.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace IntelliTect.Coalesce.CodeGeneration.Generation 5 | { 6 | public interface ICleaner 7 | { 8 | IGenerator Owner { get; set; } 9 | 10 | string TargetPath { get; set; } 11 | bool DryRun { get; set; } 12 | 13 | /// 14 | /// Perform a cleanup with the consideration that the provided list of absolute paths to files should not be removed. 15 | /// 16 | /// 17 | /// A collection of absolute paths to files that should not be modified. 18 | /// 19 | /// 20 | Task CleanupAsync(ICollection knownGoodFiles); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.CodeGeneration/Generation/Generators/GeneratorConfigAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace IntelliTect.Coalesce.CodeGeneration.Generation 4 | { 5 | /// 6 | /// Indicates that the targeted property on a generator inheriting from 7 | /// should have its value retrieved from the "generatorConfig" section in the coalesce.json file. 8 | /// 9 | [AttributeUsage(AttributeTargets.Property, Inherited = true)] 10 | public sealed class GeneratorConfigAttribute : Attribute 11 | { 12 | 13 | } 14 | } -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.CodeGeneration/Generation/Services/CompositeGeneratorServices.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.Logging; 3 | using IntelliTect.Coalesce.CodeGeneration.Configuration; 4 | using IntelliTect.Coalesce.TypeDefinition; 5 | 6 | namespace IntelliTect.Coalesce.CodeGeneration.Generation 7 | { 8 | public class CompositeGeneratorServices : GeneratorServices 9 | { 10 | public CompositeGeneratorServices( 11 | CoalesceConfiguration config, 12 | ReflectionRepository reflectionRepository, 13 | GenerationContext generationContext, 14 | IServiceProvider serviceProvider, 15 | ILoggerFactory loggerFactory) 16 | : base(config, reflectionRepository, generationContext, loggerFactory) 17 | { 18 | ServiceProvider = serviceProvider; 19 | } 20 | 21 | public IServiceProvider ServiceProvider { get; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.CodeGeneration/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:52047/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "IntelliTect.Coalesce.CodeGeneration": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "http://localhost:52052/" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.CodeGeneration/Templating/Resolution/ITemplateResolver.cs: -------------------------------------------------------------------------------- 1 | namespace IntelliTect.Coalesce.CodeGeneration.Templating.Resolution 2 | { 3 | public interface ITemplateResolver 4 | { 5 | IResolvedTemplate Resolve(TemplateDescriptor descriptor); 6 | } 7 | } -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.CodeGeneration/Templating/Resolution/ResolvedFileSystemTemplate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace IntelliTect.Coalesce.CodeGeneration.Templating.Resolution 8 | { 9 | public class ResolvedFileSystemTemplate : IResolvedTemplate 10 | { 11 | public ResolvedFileSystemTemplate(TemplateDescriptor descriptor, string resolvedPath) 12 | { 13 | TemplateDescriptor = descriptor; 14 | } 15 | 16 | public TemplateDescriptor TemplateDescriptor { get; } 17 | 18 | public bool ResolvedFromDisk => true; 19 | 20 | public string FullName { get; } 21 | 22 | public Stream GetContents() 23 | { 24 | return File.OpenRead(FullName); 25 | } 26 | 27 | public override string ToString() => FullName; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.CodeGeneration/Templating/Resolution/TemplateResolver.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.CodeGeneration.Configuration; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace IntelliTect.Coalesce.CodeGeneration.Templating.Resolution 8 | { 9 | public class TemplateResolver : ITemplateResolver 10 | { 11 | public TemplateResolver(CoalesceConfiguration config) 12 | { 13 | Config = config; 14 | } 15 | 16 | public CoalesceConfiguration Config { get; } 17 | 18 | public IResolvedTemplate Resolve(TemplateDescriptor descriptor) 19 | { 20 | return new ResolvedManifestResourceTemplate(descriptor); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.CodeGeneration/Utilities/ApplicationTimer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Text; 5 | 6 | namespace IntelliTect.Coalesce.CodeGeneration.Utilities 7 | { 8 | /// 9 | /// Just a static class with a stopwatch for measuring the total elapsed time of code generation. 10 | /// Not started by default - it gets started when the CLI starts. 11 | /// 12 | public static class ApplicationTimer 13 | { 14 | public static readonly Stopwatch Stopwatch = new Stopwatch(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.DotnetTool/IntelliTect.Coalesce.DotnetTool.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 9 | 10 | true 11 | coalesce 12 | 13 | dotnet-coalesce 14 | coalesce 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Swashbuckle/CoalesceApiSchemaFilter.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.Models; 2 | using Microsoft.OpenApi.Models; 3 | using Swashbuckle.AspNetCore.SwaggerGen; 4 | 5 | namespace IntelliTect.Coalesce.Swashbuckle 6 | { 7 | public class CoalesceApiSchemaFilter : ISchemaFilter 8 | { 9 | public void Apply(OpenApiSchema schema, SchemaFilterContext context) 10 | { 11 | if (context.Type.IsAssignableTo(typeof(ISparseDto))) 12 | { 13 | string description = "This type supports partial/surgical modifications. Properties that are entirely omitted/undefined will be left unchanged on the target object."; 14 | 15 | if (!string.IsNullOrWhiteSpace(schema.Description)) schema.Description += " " + description; 16 | else schema.Description = description; 17 | } 18 | 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Swashbuckle/CoalesceDocumentFilter.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.Models; 2 | using Microsoft.OpenApi.Models; 3 | using Swashbuckle.AspNetCore.Swagger; 4 | using Swashbuckle.AspNetCore.SwaggerGen; 5 | using System.IO; 6 | using System.Linq; 7 | 8 | namespace IntelliTect.Coalesce.Swashbuckle 9 | { 10 | public class CoalesceDocumentFilter : IDocumentFilter 11 | { 12 | public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) 13 | { 14 | // Get rid of bad schema definitions that we don't use. 15 | var defsToRemove = swaggerDoc.Components.Schemas.Keys.Where(d => 16 | // These get incorrectly put here if your API surface has file responses. 17 | d.Equals(nameof(IFile)) || 18 | d.Equals("IFileItemResult") || 19 | d.Equals(nameof(Stream)) 20 | ) 21 | .ToList(); 22 | 23 | foreach (var definition in defsToRemove) 24 | { 25 | swaggerDoc.Components.Schemas.Remove(definition); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/Fixtures/TestDbContextFixture.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.Tests.TargetClasses.TestDbContext; 2 | using IntelliTect.Coalesce.Tests.Util; 3 | 4 | namespace IntelliTect.Coalesce.Tests.Fixtures 5 | { 6 | public class TestDbContextFixture 7 | { 8 | public TestDbContextFixture() 9 | { 10 | Db = new AppDbContext(); 11 | CrudContext = new CrudContext(Db, () => new System.Security.Claims.ClaimsPrincipal()) 12 | { 13 | ReflectionRepository = ReflectionRepositoryFactory.Reflection 14 | }; 15 | } 16 | 17 | public AppDbContext Db { get; } 18 | public CrudContext CrudContext { get; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/TargetClasses/.editorconfig: -------------------------------------------------------------------------------- 1 | # Suppress: EC112 2 | 3 | [*.cs] 4 | 5 | # RCS1020: Simplify Nullable to T?. 6 | dotnet_diagnostic.RCS1020.severity = none -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/TargetClasses/Bools.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace IntelliTect.Coalesce.Tests.TargetClasses 5 | { 6 | internal class Bools 7 | { 8 | public bool NonNullableKeywordName { get; set; } 9 | public Boolean NonNullableClassName { get; set; } 10 | public bool? NullableQuestionMarkKeywordName { get; set; } 11 | public Boolean? NullableQuestionMarkClassName { get; set; } 12 | public Nullable NullableGenericKeywordName { get; set; } 13 | public Nullable NullableGenericClassName { get; set; } 14 | 15 | public bool[] Array { get; set; } 16 | public bool?[] ArrayNullable { get; set; } 17 | public ICollection Collection { get; set; } 18 | public ICollection CollectionNullableItems { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/TargetClasses/ComplexInheritance.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace IntelliTect.Coalesce.Tests.TargetClasses 7 | { 8 | // IEnumerable is technically duplicated here. 9 | // We specify it explicitly, and IEnumerable<> also implements IEnumerable. 10 | public class ComplexInheritance : IEnumerable, IEnumerable, IAmInheritedMultipleTimes, IAmInheritedMultipleTimes 11 | { 12 | public IEnumerator GetEnumerator() 13 | { 14 | throw new NotImplementedException(); 15 | } 16 | 17 | IEnumerator IEnumerable.GetEnumerator() 18 | { 19 | throw new NotImplementedException(); 20 | } 21 | } 22 | 23 | public interface IAmInheritedMultipleTimes : IHaveMultipleTypes 24 | { 25 | 26 | } 27 | 28 | public interface IHaveMultipleTypes 29 | { 30 | 31 | } 32 | 33 | public interface IAmSimple 34 | { 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/TargetClasses/Github31.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace IntelliTect.Coalesce.Tests.TargetClasses.Github31 6 | { 7 | // https://github.com/IntelliTect/Coalesce/issues/31 8 | internal class Person 9 | { 10 | public int PersonId { get; set; } 11 | public string Name { get; set; } 12 | 13 | public DateTimeOffset? BirthDate { get; set; } 14 | 15 | [Coalesce] 16 | public static ICollection GetMyPeeps() 17 | { 18 | return null; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/TargetClasses/TestDbContext/CaseProduct.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace IntelliTect.Coalesce.Tests.TargetClasses.TestDbContext 5 | { 6 | [Table("CaseProduct")] 7 | public class CaseProduct 8 | { 9 | public int CaseProductId { get; set; } 10 | public int CaseId { get; set; } 11 | 12 | [Search] 13 | [DefaultOrderBy(FieldOrder = 2)] 14 | public Case Case { get; set; } 15 | 16 | public int ProductId { get; set; } 17 | 18 | [Search] 19 | [DefaultOrderBy(FieldOrder = 1)] 20 | public Product Product { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/TargetClasses/TestDbContext/ComplexModelDependent.cs: -------------------------------------------------------------------------------- 1 | namespace IntelliTect.Coalesce.Tests.TargetClasses.TestDbContext 2 | { 3 | public class ComplexModelDependent 4 | { 5 | public int Id { get; set; } 6 | 7 | // Foreign key without nav prop (discovered by ForeignKeyAttribute on the inverse collection navigation). 8 | public int ParentId { get; set; } 9 | 10 | public string Name { get; set; } 11 | 12 | [Coalesce] 13 | public CaseDtoStandalone SameMethodNameAsMethodOnDifferentType(CaseDtoStandalone input) => input; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/TargetClasses/TestDbContext/EnumPk.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | using System.ComponentModel.DataAnnotations; 5 | using System.Text; 6 | 7 | namespace IntelliTect.Coalesce.Tests.TargetClasses.TestDbContext 8 | { 9 | public enum EnumPkId 10 | { 11 | Value0 = 0, 12 | Value1 = 1, 13 | Value10 = 10, 14 | } 15 | 16 | public class EnumPk 17 | { 18 | [Key] 19 | [DatabaseGenerated(DatabaseGeneratedOption.None)] 20 | public EnumPkId EnumPkId { get; set; } 21 | 22 | public string Name { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/TargetClasses/TestDbContext/InternalUseClass.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.DataAnnotations; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace IntelliTect.Coalesce.Tests.TargetClasses.TestDbContext 7 | { 8 | internal class InternalClass 9 | { 10 | } 11 | 12 | [InternalUse] 13 | public class InternalUseClass 14 | { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/TargetClasses/TestDbContext/MultipleParents.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace IntelliTect.Coalesce.Tests.TargetClasses.TestDbContext 4 | { 5 | public class MultipleParents 6 | { 7 | public int Id { get; set; } 8 | 9 | public int? Parent1Id { get; set; } 10 | public Parent1 Parent1 { get; set; } 11 | 12 | public int? Parent2Id { get; set; } 13 | public Parent2 Parent2 { get; set; } 14 | } 15 | 16 | public class Parent1 17 | { 18 | public int Id { get; set; } 19 | public List Children { get; set; } 20 | } 21 | 22 | public class Parent2 23 | { 24 | public int Id { get; set; } 25 | public List Children { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/TargetClasses/TestDbContext/Records.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.DataAnnotations; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace IntelliTect.Coalesce.Tests.TargetClasses.TestDbContext; 7 | 8 | public record PositionalRecord(string String, int Num) 9 | { 10 | public DateTime Date { get; init; } 11 | } 12 | 13 | public record InitRecordWithDefaultCtor 14 | { 15 | public string String { get; init; } 16 | 17 | [Read("ReadRole"), Edit("EditRole")] 18 | public int Num { get; init; } 19 | 20 | [DtoIncludes("asdf"), DtoExcludes("qwert")] 21 | public PositionalRecord NestedRecord { get; init; } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/TargetClasses/TestDbContext/RecursiveHierarchy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | #nullable enable 9 | 10 | namespace IntelliTect.Coalesce.Tests.TargetClasses.TestDbContext 11 | { 12 | public class RecursiveHierarchy 13 | { 14 | public int Id { get; set; } 15 | 16 | public string? Name { get; set; } 17 | 18 | public int? ParentId { get; set; } 19 | public RecursiveHierarchy? Parent { get; set; } 20 | 21 | [InverseProperty(nameof(Parent))] 22 | public List Children { get; set; } = new(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/TargetClasses/TestDbContext/RequiredAndInitModel.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace IntelliTect.Coalesce.Tests.TargetClasses.TestDbContext 3 | { 4 | public class RequiredAndInitModel 5 | { 6 | public int Id { get; set; } 7 | 8 | public required string RequiredRef { get; set; } 9 | public required int RequiredValue { get; set; } 10 | public required string RequiredInitRef { get; init; } 11 | public required int RequiredInitValue { get; init; } 12 | public string InitRef { get; init; } 13 | public int InitValue { get; init; } 14 | public int? NullableInitValue { get; init; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/TargetClasses/TestDbContext/RoleNames.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace IntelliTect.Coalesce.Tests.TargetClasses.TestDbContext 6 | { 7 | public static class RoleNames 8 | { 9 | public const string User = "User"; 10 | public const string Admin = "Admin"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/TargetClasses/TestDbContext/Sibling.cs: -------------------------------------------------------------------------------- 1 | namespace IntelliTect.Coalesce.Tests.TargetClasses.TestDbContext 2 | { 3 | public class Sibling 4 | { 5 | public int SiblingId { get; set; } 6 | 7 | public int PersonId { get; set; } 8 | public Person Person { get; set; } 9 | 10 | public int PersonTwoId { get; set; } 11 | public Person PersonTwo { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/TargetClasses/TestDbContext/Test.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.DataAnnotations; 2 | using IntelliTect.Coalesce.Tests.TargetClasses.TestDbContext; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace IntelliTect.Coalesce.Tests.TargetClasses 8 | { 9 | // This class is deliberately named "Test", since the name "Test" also occurs in its namespace. 10 | // This class is intended for testing resolutions to issues like https://github.com/IntelliTect/Coalesce/issues/28 11 | public class Test 12 | { 13 | public int TestId { get; set; } 14 | 15 | public int ComplexModelId { get; set; } 16 | public ComplexModel ComplexModel { get; set; } 17 | 18 | [ListText] 19 | public string TestName { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/TargetClasses/TestDbContext/ZipCode.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations.Schema; 2 | using System.ComponentModel.DataAnnotations; 3 | using IntelliTect.Coalesce.DataAnnotations; 4 | 5 | namespace IntelliTect.Coalesce.Tests.TargetClasses.TestDbContext 6 | { 7 | public class ZipCode 8 | { 9 | [Key] 10 | [DatabaseGenerated(DatabaseGeneratedOption.None)] 11 | [ListText] 12 | public string Zip { get; set; } = null!; 13 | 14 | public string State { get; set; } = null!; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/Tests/ApplicationTests.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.Tests.TargetClasses.TestDbContext; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Xunit; 9 | 10 | namespace IntelliTect.Coalesce.Tests.Tests 11 | { 12 | public class ApplicationTests 13 | { 14 | [Fact] 15 | public void AddCoalesce_DoesNotExplicitlyRequireWebServices() 16 | { 17 | // Ensures that esoteric testing scenarios can setup Coalesce services 18 | // without a full aspnetcore host builder. 19 | 20 | var services = new ServiceCollection(); 21 | 22 | services.AddDbContext(); 23 | services.AddCoalesce(b => b.AddContext()); 24 | 25 | var sp = services.BuildServiceProvider(); 26 | 27 | Assert.NotNull(sp.GetRequiredService>()); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/Tests/ClonerTest.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.Helpers; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | 8 | namespace IntelliTect.Coalesce.Tests 9 | { 10 | public class ClonerTest 11 | { 12 | [Fact] 13 | public void CopyTest() 14 | { 15 | var src = new TestClass 16 | { 17 | I = 34, 18 | S = "My String", 19 | C = new TestClass() 20 | }; 21 | src.C.I = 12; 22 | src.Field = "my Field"; 23 | var dest = src.Copy(); 24 | 25 | Assert.Equal(src.I, dest.I); 26 | Assert.Equal(src.S, dest.S); 27 | Assert.Equal(src.Field, dest.Field); 28 | Assert.Same(src.C, dest.C); 29 | } 30 | 31 | 32 | 33 | public class TestClass 34 | { 35 | public string S { get; set; } 36 | public int I { get; set; } 37 | public TestClass C { get; set; } 38 | public double Test() { return 1.0; } 39 | 40 | public string Field; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Tests/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "methodDisplay": "method" 3 | } -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce.Vue/IntelliTect.Coalesce.Vue.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vue.js shared code for server projects for IntelliTect.Coalesce 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Api/Behaviors/BehaviorsModelBinderProvider.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.TypeDefinition; 2 | using Microsoft.AspNetCore.Mvc.ModelBinding; 3 | using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; 4 | using System; 5 | 6 | namespace IntelliTect.Coalesce.Api.Behaviors 7 | { 8 | public class BehaviorsModelBinderProvider : IModelBinderProvider 9 | { 10 | public IModelBinder? GetBinder(ModelBinderProviderContext context) 11 | { 12 | if (context == null) 13 | { 14 | throw new ArgumentNullException(nameof(context)); 15 | } 16 | 17 | var typeViewModel = new ReflectionTypeViewModel(context.Metadata.ModelType); 18 | if (!typeViewModel.IsA(typeof(IBehaviors<>))) return null; 19 | 20 | return new BinderTypeModelBinder(typeof(BehaviorsModelBinder)); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Api/Behaviors/IBehaviorsFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using IntelliTect.Coalesce.TypeDefinition; 3 | using Microsoft.AspNetCore.Hosting.Server; 4 | 5 | namespace IntelliTect.Coalesce.Api.Behaviors 6 | { 7 | public interface IBehaviorsFactory 8 | { 9 | object GetBehaviors(ClassViewModel servedType, ClassViewModel declaredFor); 10 | 11 | Type GetBehaviorsType(ClassViewModel servedType, ClassViewModel declaredFor); 12 | 13 | IBehaviors GetBehaviors(ClassViewModel declaredFor) 14 | where TServed : class; 15 | 16 | object GetDefaultBehaviors(ClassViewModel servedType); 17 | 18 | } 19 | } -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Api/Behaviors/IEntityFrameworkBehaviors.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace IntelliTect.Coalesce 7 | { 8 | /// 9 | /// Marker interface for the behaviors that will be used when handling entity types for which no custom behaviors are resolved. 10 | /// 11 | /// The entity type handled. 12 | /// The context that serves the entity. 13 | public interface IEntityFrameworkBehaviors : IBehaviors 14 | where T : class 15 | where TContext : DbContext 16 | { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Api/Behaviors/SaveKind.cs: -------------------------------------------------------------------------------- 1 | namespace IntelliTect.Coalesce 2 | { 3 | public enum SaveKind 4 | { 5 | Create, 6 | Update, 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Api/Controllers/IApiActionFilter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Filters; 2 | 3 | namespace IntelliTect.Coalesce.Api.Controllers 4 | { 5 | /// 6 | /// Marker interface for the filter that will handle error reponses and model validation for API Controllers. 7 | /// 8 | public interface IApiActionFilter : IActionFilter, IAlwaysRunResultFilter { } 9 | } 10 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Api/CrudStrategy/IStandardCrudStrategy.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.TypeDefinition; 2 | using Microsoft.EntityFrameworkCore; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Security.Claims; 6 | using System.Text; 7 | 8 | namespace IntelliTect.Coalesce.Api 9 | { 10 | public interface IStandardCrudStrategy 11 | { 12 | /// 13 | /// The user making the request. 14 | /// 15 | ClaimsPrincipal? User { get; } 16 | 17 | /// 18 | /// A ClassViewModel representing the type T that is handled by these strategies. 19 | /// 20 | ClassViewModel ClassViewModel { get; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Api/DataSources/DataSourceModelBinderProvider.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.TypeDefinition; 2 | using Microsoft.AspNetCore.Mvc.ModelBinding; 3 | using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; 4 | using System; 5 | 6 | namespace IntelliTect.Coalesce.Api.DataSources 7 | { 8 | public class DataSourceModelBinderProvider : IModelBinderProvider 9 | { 10 | public IModelBinder? GetBinder(ModelBinderProviderContext context) 11 | { 12 | if (context == null) 13 | { 14 | throw new ArgumentNullException(nameof(context)); 15 | } 16 | 17 | var typeViewModel = new ReflectionTypeViewModel(context.Metadata.ModelType); 18 | if (!typeViewModel.IsA(typeof(IDataSource<>))) return null; 19 | 20 | return new BinderTypeModelBinder(typeof(DataSourceModelBinder)); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Api/DataSources/DataSourceNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.TypeDefinition; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace IntelliTect.Coalesce.Api.DataSources 7 | { 8 | #pragma warning disable RCS1194 // Implement exception constructors. 9 | public class DataSourceNotFoundException : Exception 10 | #pragma warning restore RCS1194 // Implement exception constructors. 11 | { 12 | private readonly ClassViewModel servedType; 13 | private readonly ClassViewModel declaredFor; 14 | private readonly string dataSourceName; 15 | 16 | public DataSourceNotFoundException(ClassViewModel servedType, ClassViewModel declaredFor, string dataSourceName) 17 | { 18 | this.servedType = servedType; 19 | this.declaredFor = declaredFor; 20 | this.dataSourceName = dataSourceName; 21 | } 22 | 23 | public override string Message => $"A DataSource named {dataSourceName} declared for {declaredFor} that serves type {servedType} could not be found"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Api/DataSources/DefaultDataSourceAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace IntelliTect.Coalesce 6 | { 7 | /// 8 | /// Indicate that an IDataSource<T> is the default implementation for its type T. 9 | /// This DataSource will displace the standard implementation for its served type. 10 | /// Any references to "Default", and any requests with no specified DataSource, will use this type. 11 | /// 12 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] 13 | public sealed class DefaultDataSourceAttribute : Attribute 14 | { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Api/DataSources/IDataSource.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.Models; 2 | using System.Threading.Tasks; 3 | 4 | namespace IntelliTect.Coalesce 5 | { 6 | 7 | public interface IDataSource 8 | where T : class 9 | { 10 | Task> GetItemAsync(object id, IDataSourceParameters parameters); 11 | 12 | Task> GetMappedItemAsync(object id, IDataSourceParameters parameters) 13 | where TDto : class, IResponseDto, new(); 14 | 15 | Task> GetListAsync(IListParameters parameters); 16 | 17 | Task> GetMappedListAsync(IListParameters parameters) 18 | where TDto : class, IResponseDto, new(); 19 | 20 | Task> GetCountAsync(IFilterParameters parameters); 21 | } 22 | } -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Api/DataSources/IDataSourceFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using IntelliTect.Coalesce.TypeDefinition; 3 | 4 | namespace IntelliTect.Coalesce.Api.DataSources 5 | { 6 | public interface IDataSourceFactory 7 | { 8 | object GetDataSource(ClassViewModel servedType, ClassViewModel declaredFor, string? dataSourceName = null); 9 | 10 | Type GetDataSourceType(ClassViewModel servedType, ClassViewModel declaredFor, string? dataSourceName = null); 11 | 12 | IDataSource GetDataSource(ClassViewModel declaredFor, string? dataSourceName) 13 | where TServed : class; 14 | 15 | IDataSource GetDataSource(string? dataSourceName) 16 | where TServed : class 17 | where TDeclaredFor : class; 18 | 19 | object GetDefaultDataSource(ClassViewModel servedType, ClassViewModel declaredFor); 20 | 21 | IDataSource GetDefaultDataSource(ClassViewModel declaredFor) 22 | where TServed : class; 23 | 24 | IDataSource GetDefaultDataSource() 25 | where TServed : class 26 | where TDeclaredFor : class; 27 | } 28 | } -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Api/DataSources/IEntityFrameworkDataSource.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace IntelliTect.Coalesce 7 | { 8 | /// 9 | /// Marker interface for the data source that will be used when serving entity types for which no custom data source is resolved. 10 | /// 11 | /// The entity type served 12 | /// The context that serves the entity. 13 | public interface IEntityFrameworkDataSource : IDataSource 14 | where T : class 15 | where TContext : DbContext 16 | { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Api/DataSources/IResultTransformer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace IntelliTect.Coalesce 5 | { 6 | public interface IResultTransformer 7 | where T : class 8 | { 9 | Task TransformResultsAsync(IReadOnlyList results, IDataSourceParameters parameters); 10 | } 11 | } -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Api/DeclaredForAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace IntelliTect.Coalesce 4 | { 5 | /// 6 | /// Used to hint that a IDataSource or IBehaviors instance is declared for the given type. 7 | /// This is used both when defining such a class for an IClassDto as well as when such an instance needs to be model-bound. 8 | /// 9 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Class)] 10 | public class DeclaredForAttribute : Attribute 11 | { 12 | public DeclaredForAttribute(Type declaredFor) 13 | { 14 | DeclaredFor = declaredFor ?? throw new ArgumentNullException(nameof(declaredFor)); 15 | } 16 | 17 | public Type DeclaredFor { get; } 18 | } 19 | } -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Api/Parameters/DataSourceParameters.cs: -------------------------------------------------------------------------------- 1 | namespace IntelliTect.Coalesce.Api 2 | { 3 | public class DataSourceParameters : IDataSourceParameters 4 | { 5 | /// 6 | public string? Includes { get; set; } 7 | 8 | /// 9 | public string? DataSource { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Api/Parameters/FilterParameters.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace IntelliTect.Coalesce.Api 5 | { 6 | public class FilterParameters : DataSourceParameters, IFilterParameters 7 | { 8 | /// 9 | public string? Search { get; set; } 10 | 11 | /// 12 | public Dictionary Filter { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); 13 | 14 | /// 15 | IDictionary IFilterParameters.Filter => Filter; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Api/Parameters/IDataSourceParameters.cs: -------------------------------------------------------------------------------- 1 | // Explicitly in IntelliTect.Coalesce to simplify typical using statements 2 | namespace IntelliTect.Coalesce 3 | { 4 | public interface IDataSourceParameters 5 | { 6 | /// 7 | /// The "include" string specified by the client. 8 | /// Used primarily for property-level DTO trimming. 9 | /// Specifying "none" will prevent the default query behavior of 10 | /// including all immediate relations when using the StandardDataSource. 11 | /// 12 | string? Includes { get; } 13 | 14 | /// 15 | /// The name of the data source that was requested by the client. 16 | /// This could be different than the name of the data source that gets resolved 17 | /// (in the event of overridden default data sources). 18 | /// 19 | string? DataSource { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Api/Parameters/IFilterParameters.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | // Explicitly in IntelliTect.Coalesce to simplify typical using statements 6 | namespace IntelliTect.Coalesce 7 | { 8 | public interface IFilterParameters : IDataSourceParameters 9 | { 10 | /// 11 | /// A free-form search string. 12 | /// 13 | string? Search { get; } 14 | 15 | /// 16 | /// A mapping of values, keyed by field name, on which to filter. 17 | /// It is the responsibility of the consumer to decide how to interpret these values. 18 | /// 19 | IDictionary Filter { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Api/Parameters/SortDirection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace IntelliTect.Coalesce 6 | { 7 | public enum SortDirection 8 | { 9 | /// 10 | /// Ascending 11 | /// 12 | Asc = 1, 13 | 14 | /// 15 | /// Descending 16 | /// 17 | Desc = 2, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Application/DefaultTimeZoneResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace IntelliTect.Coalesce 6 | { 7 | internal class StaticTimeZoneResolver : ITimeZoneResolver 8 | { 9 | public StaticTimeZoneResolver(TimeZoneInfo timeZoneInfo) { 10 | TimeZoneInfo = timeZoneInfo; 11 | } 12 | 13 | public TimeZoneInfo TimeZoneInfo { get; } 14 | 15 | public TimeZoneInfo GetTimeZoneInfo() => TimeZoneInfo; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Application/ITimeZoneResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace IntelliTect.Coalesce 6 | { 7 | public interface ITimeZoneResolver 8 | { 9 | TimeZoneInfo GetTimeZoneInfo(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/DataAnnotations/ClientValidationAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace IntelliTect.Coalesce.DataAnnotations 7 | { 8 | [System.AttributeUsage(System.AttributeTargets.Property)] 9 | public class ClientValidationAttribute: System.Attribute 10 | { 11 | public bool IsRequired { get; set; } 12 | 13 | // Note: nullable propeties can't be used here because nullable values can't be used as attribute initializers. 14 | public double MinValue { get; set; } = double.MaxValue; 15 | public double MaxValue { get; set; } = double.MinValue; 16 | public int MinLength { get; set; } = int.MaxValue; 17 | public int MaxLength { get; set; } = int.MinValue; 18 | 19 | public string? Pattern { get; set; } 20 | public bool IsEmail { get; set; } 21 | public bool IsPhoneUs { get; set; } 22 | 23 | /// 24 | /// Gets or sets an error message to associate with a validation control if validation fails. 25 | /// 26 | public string? ErrorMessage { get; set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/DataAnnotations/CoalesceAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace IntelliTect.Coalesce 6 | { 7 | /// 8 | /// The targeted class or method should be exposed by Coalesce. 9 | /// Different types will be exposed in different ways. See documentation for details. 10 | /// 11 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Interface | AttributeTargets.Enum)] 12 | public sealed class CoalesceAttribute : Attribute 13 | { 14 | /// 15 | /// When placed on a type, overrides the name of the type used in client-side code. 16 | /// 17 | public string? ClientTypeName { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/DataAnnotations/CreateAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using IntelliTect.Coalesce.Helpers; 3 | 4 | namespace IntelliTect.Coalesce.DataAnnotations 5 | { 6 | /// 7 | /// 8 | /// When placed on an entity or custom class exposed by Coalesce, 9 | /// controls the permissions for saving new instances of the model via the /save or /bulkSave endpoints. 10 | /// 11 | /// 12 | [AttributeUsage(AttributeTargets.Class)] 13 | public sealed class CreateAttribute : SecurityAttribute 14 | { 15 | public CreateAttribute() 16 | { 17 | } 18 | 19 | public CreateAttribute(SecurityPermissionLevels permissionLevel) 20 | { 21 | PermissionLevel = permissionLevel; 22 | } 23 | 24 | public CreateAttribute(params string[] roles) 25 | { 26 | Roles = string.Join(",", roles); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/DataAnnotations/CreateControllerAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace IntelliTect.Coalesce.DataAnnotations 7 | { 8 | /// 9 | /// Allows specifying the types of controllers to create. Not including will create all. 10 | /// 11 | [System.AttributeUsage(System.AttributeTargets.Class)] 12 | [Obsolete("Use security attributes (Read/Edit/Create/Delete) or InternalUse to hide parts of entities or whole entities from Coalesce APIs")] 13 | public class CreateControllerAttribute : System.Attribute 14 | { 15 | public bool WillCreateView { get; set; } 16 | public bool WillCreateApi { get; set; } 17 | 18 | public CreateControllerAttribute(bool willCreateView = true, bool willCreateApi = true) 19 | { 20 | WillCreateView = willCreateView; 21 | WillCreateApi = willCreateApi; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/DataAnnotations/DateTypeAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace IntelliTect.Coalesce.DataAnnotations 7 | { 8 | /// 9 | /// Allows specifying the type of date to contain. 10 | /// 11 | [System.AttributeUsage(System.AttributeTargets.Property)] 12 | public class DateTypeAttribute : System.Attribute 13 | { 14 | public enum DateTypes 15 | { 16 | DateTime = 0, 17 | DateOnly = 1, 18 | TimeOnly = 2, 19 | } 20 | 21 | public DateTypes DateType { get; set; } 22 | 23 | public DateTypeAttribute(DateTypes dateType = DateTypes.DateTime) 24 | { 25 | this.DateType = dateType; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/DataAnnotations/DeleteAttribute.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.Helpers; 2 | using System; 3 | 4 | namespace IntelliTect.Coalesce.DataAnnotations 5 | { 6 | /// 7 | /// 8 | /// When placed on an entity or custom class exposed by Coalesce, 9 | /// controls the permissions for the deleting existing instances of the model via the /delete or /bulkSave endpoints. 10 | /// 11 | /// 12 | [AttributeUsage(AttributeTargets.Class)] 13 | public class DeleteAttribute : SecurityAttribute 14 | { 15 | public DeleteAttribute() 16 | { 17 | } 18 | 19 | public DeleteAttribute(SecurityPermissionLevels permissionLevel) 20 | { 21 | PermissionLevel = permissionLevel; 22 | } 23 | 24 | public DeleteAttribute(params string[] roles) 25 | { 26 | Roles = string.Join(",", roles); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/DataAnnotations/DtoExcludesAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace IntelliTect.Coalesce.DataAnnotations 4 | { 5 | /// 6 | /// Specify that this property is only used on specific views of the content. 7 | /// 8 | [AttributeUsage(AttributeTargets.Property)] 9 | public class DtoExcludesAttribute : Attribute 10 | { 11 | /// 12 | /// Comma-delimited list of content views this property should be excluded from. 13 | /// 14 | public string ContentViews { get; set; } = ""; 15 | 16 | public DtoExcludesAttribute( ) 17 | { 18 | 19 | } 20 | 21 | public DtoExcludesAttribute(string contentViews) 22 | { 23 | ContentViews = contentViews; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/DataAnnotations/DtoIncludesAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace IntelliTect.Coalesce.DataAnnotations 4 | { 5 | /// 6 | /// Specify that this property is only used on specific views of the content. 7 | /// 8 | [AttributeUsage(AttributeTargets.Property)] 9 | public class DtoIncludesAttribute : Attribute 10 | { 11 | /// 12 | /// Comma-delimited list of content views this property should be included on. 13 | /// 14 | public string ContentViews { get; set; } = ""; 15 | 16 | public DtoIncludesAttribute() 17 | { 18 | 19 | } 20 | 21 | public DtoIncludesAttribute( string contentViews ) 22 | { 23 | ContentViews = contentViews; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/DataAnnotations/HiddenAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace IntelliTect.Coalesce.DataAnnotations 7 | { 8 | /// 9 | /// Allows this property to be hidden on the list or editor or both. 10 | /// 11 | [System.AttributeUsage(System.AttributeTargets.Property | System.AttributeTargets.Method)] 12 | public class HiddenAttribute : System.Attribute 13 | { 14 | [Flags] 15 | public enum Areas 16 | { 17 | None = 0, 18 | List = 1 << 0, 19 | Edit = 1 << 1, 20 | All = List | Edit, 21 | } 22 | 23 | public Areas Area { get; set; } 24 | 25 | public HiddenAttribute(Areas area = Areas.All) 26 | { 27 | this.Area = area; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/DataAnnotations/InjectAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace IntelliTect.Coalesce.DataAnnotations 7 | { 8 | /// 9 | /// When placed on a method parameter declaration, causes the parameter to be injected from the application's IServiceContainer. 10 | /// 11 | [System.AttributeUsage(System.AttributeTargets.Parameter)] 12 | public class InjectAttribute : Attribute 13 | { 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/DataAnnotations/InternalUseAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using static System.AttributeTargets; 6 | 7 | namespace IntelliTect.Coalesce.DataAnnotations 8 | { 9 | /// 10 | /// Types and members marked [InternalUse] are not exposed by Coalesce's generated APIs. 11 | /// 12 | [System.AttributeUsage(Property | Method | Class | Struct)] 13 | public class InternalUseAttribute : Attribute 14 | { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/DataAnnotations/ListTextAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace IntelliTect.Coalesce.DataAnnotations 7 | { 8 | /// 9 | /// The property marked with the attribute will be shown in drop down lists. 10 | /// 11 | [System.AttributeUsage(System.AttributeTargets.Property)] 12 | public class ListTextAttribute : System.Attribute 13 | { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/DataAnnotations/LoadFromDataSourceAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace IntelliTect.Coalesce.DataAnnotations 6 | { 7 | /// 8 | /// Specify that the targeted model instance method should load the instance it is called on 9 | /// from the specified data source when invoked from an API endpoint. 10 | /// By default, whatever the default data source for the model's type will be used. 11 | /// 12 | [AttributeUsage(AttributeTargets.Method)] 13 | [Obsolete("LoadFromDataSourceAttribute has been merged into ExecuteAttribute")] 14 | public sealed class LoadFromDataSourceAttribute : Attribute 15 | { 16 | public LoadFromDataSourceAttribute(Type dataSourceType) 17 | { 18 | DataSourceType = dataSourceType; 19 | } 20 | 21 | public Type DataSourceType { get; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/DataAnnotations/ManyToManyAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace IntelliTect.Coalesce.DataAnnotations 7 | { 8 | /// 9 | /// Identifies a property as a many to many relationship and creates a secondary property on the view model for 10 | /// viewing the collection of target items directly. 11 | /// 12 | [System.AttributeUsage(AttributeTargets.Property)] 13 | public class ManyToManyAttribute : System.Attribute 14 | { 15 | public string CollectionName { get; } 16 | 17 | /// 18 | /// The name of the navigation property on the middle entity that points at the far side of the many-to-many relationship. 19 | /// 20 | public string? FarNavigationProperty { get; set; } 21 | 22 | public ManyToManyAttribute(string collectionName) 23 | { 24 | CollectionName = collectionName; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/DataAnnotations/ReadOnlyApiAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace IntelliTect.Coalesce.DataAnnotations 7 | { 8 | /// 9 | /// Properties marked ReadOnlyApi are not saved. 10 | /// 11 | [System.AttributeUsage(System.AttributeTargets.Property)] 12 | [Obsolete("Mark a property with the [Read] attribute with no [Edit] attribute to make it read-only")] 13 | public class ReadOnlyApiAttribute : System.Attribute 14 | { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/DataAnnotations/RestrictAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace IntelliTect.Coalesce.DataAnnotations 4 | { 5 | [AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = true)] 6 | public sealed class RestrictAttribute : Attribute 7 | where T : IPropertyRestriction 8 | { 9 | // Originally, there was a design of this attribute that had the attribute itself implementing 10 | // `IPropertyRestriction`, but this design of the attribute itself implementing the functionality 11 | // presents with a major problem: 12 | 13 | // We'd like to have a `IPropertyRestriction` instance per mapping operation 14 | // so that instances can be constructed with dependency injection and also cache 15 | // state for reuse within the same mapping operation. This doesn't work with 16 | // attribute instances which are instantiated by the runtime. 17 | 18 | // So, this attribute only functions as a marker attribute for discovery of T. 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/DataAnnotations/ServiceAttribute.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace IntelliTect.Coalesce 7 | { 8 | /// 9 | /// 10 | /// The targeted class or interface will be exposed as a service by Coalesce. 11 | /// 12 | /// 13 | /// All methods of a targeted interface, or all public methods marked with of a targeted class, 14 | /// will have an API endpoint generated. 15 | /// 16 | /// 17 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] 18 | public sealed class ServiceAttribute : Attribute 19 | { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/DataAnnotations/StandaloneEntityAttribute.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace IntelliTect.Coalesce 7 | { 8 | 9 | /// 10 | /// 11 | /// The targeted class or interface will be exposed as a standalone entity by Coalesce. 12 | /// 13 | /// 14 | /// The class will behave as if it is a standard -mapped entity type, 15 | /// except it must provide its own definition of a 16 | /// and (behaviors are optional for read-only types). 17 | /// 18 | /// 19 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] 20 | public sealed class StandaloneEntityAttribute : Attribute 21 | { 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Helpers/Cloner.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.TypeDefinition; 2 | using System; 3 | using System.Collections.Concurrent; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Threading.Tasks; 8 | 9 | namespace IntelliTect.Coalesce.Helpers 10 | { 11 | public static class Cloner 12 | { 13 | /// 14 | /// Makes a shallow copy of an object. 15 | /// 16 | /// 17 | /// 18 | public static T Copy(this T source) where T : class 19 | { 20 | return (T)_MemberwiseCloneMethodInfo.Invoke(source, null)!; 21 | } 22 | 23 | private static MethodInfo _MemberwiseCloneMethodInfo = typeof(object) 24 | .GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance)!; 25 | } 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Helpers/EnumExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Threading.Tasks; 7 | 8 | namespace IntelliTect.Coalesce.Helpers 9 | { 10 | public static class EnumExtensions 11 | { 12 | public static string? GetDisplayName(this Enum value) 13 | { 14 | return value.GetType() 15 | .GetMember(value.ToString()) 16 | .First() 17 | .GetCustomAttribute() 18 | ?.GetName() 19 | ?? value.ToString(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Helpers/Search/SearchableProperty.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.TypeDefinition; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Security.Claims; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace IntelliTect.Coalesce.Helpers.Search 11 | { 12 | public abstract class SearchableProperty 13 | { 14 | public SearchableProperty(PropertyViewModel prop) 15 | { 16 | Property = prop; 17 | } 18 | 19 | public PropertyViewModel Property { get; protected set; } 20 | 21 | protected virtual string PropertyNamePath => Property.Name; 22 | 23 | public abstract IEnumerable<(PropertyViewModel property, Expression statement)> GetLinqSearchStatements( 24 | CrudContext context, Expression propertyParent, string rawSearchTerm); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/IntelliTect.Coalesce.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Core framework library for IntelliTect.Coalesce 5 | AnyCPU 6 | 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Mapping/IMappingContext.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.DataAnnotations; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Security.Claims; 5 | 6 | namespace IntelliTect.Coalesce 7 | { 8 | public interface IMappingContext 9 | { 10 | string? Includes { get; } 11 | ClaimsPrincipal User { get; } 12 | 13 | bool IsInRoleCached(string role); 14 | 15 | void AddMapping(object sourceObject, IncludeTree? includeTree, object mappedObject); 16 | 17 | bool TryGetMapping( 18 | object sourceObject, 19 | IncludeTree? includeTree, 20 | [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] 21 | out TDto? mappedObject 22 | ) 23 | where TDto : class; 24 | 25 | IPropertyRestriction GetPropertyRestriction(Type type); 26 | TRestriction GetPropertyRestriction() 27 | where TRestriction : IPropertyRestriction 28 | => (TRestriction)GetPropertyRestriction(typeof(TRestriction)); 29 | } 30 | } -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Mapping/IncludeTree/IIncludedSeparatelyQueryable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace IntelliTect.Coalesce.Mapping.IncludeTrees 8 | { 9 | public interface IIncludedSeparatelyQueryable : IQueryable 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Mapping/IncludeTree/IncludedSeparatelyQueryable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Threading.Tasks; 7 | 8 | namespace IntelliTect.Coalesce.Mapping.IncludeTrees 9 | { 10 | public class IncludedSeparatelyQueryable : IIncludedSeparatelyQueryable 11 | { 12 | private readonly IQueryable _queryable; 13 | 14 | public IncludedSeparatelyQueryable(IQueryable queryable) 15 | { 16 | _queryable = queryable; 17 | } 18 | 19 | public Expression Expression => _queryable.Expression; 20 | public Type ElementType => _queryable.ElementType; 21 | public IQueryProvider Provider => _queryable.Provider; 22 | 23 | public IEnumerator GetEnumerator() => _queryable.GetEnumerator(); 24 | 25 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Models/ApiResult.cs: -------------------------------------------------------------------------------- 1 | namespace IntelliTect.Coalesce.Models 2 | { 3 | public class ApiResult 4 | { 5 | public bool WasSuccessful { get; set; } = true; 6 | 7 | public string? Message { get; set; } 8 | 9 | /// 10 | /// Controls the shape of the DTO mapping of the result object. 11 | /// 12 | [System.Text.Json.Serialization.JsonIgnore] 13 | [System.Runtime.Serialization.IgnoreDataMember] // for newtonsoft 14 | public IncludeTree? IncludeTree { get; set; } 15 | 16 | public ApiResult() { } 17 | 18 | public ApiResult(bool wasSuccessful, string? message = null) 19 | { 20 | WasSuccessful = wasSuccessful; 21 | Message = message; 22 | } 23 | 24 | public ApiResult(string? errorMessage) : this(false, errorMessage) { } 25 | 26 | public ApiResult(ApiResult result) : this(result.WasSuccessful, result.Message) 27 | { 28 | IncludeTree = result.IncludeTree; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Models/FileParameter.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.Api.Controllers; 2 | using System.IO; 3 | 4 | namespace IntelliTect.Coalesce.Models 5 | { 6 | public class FileParameter : IFile 7 | { 8 | public required byte[] Content { get; set; } 9 | 10 | public string? ContentType { get; set; } 11 | 12 | public string? Name { get; set; } 13 | 14 | public long Length => Content.Length; 15 | 16 | bool IFile.ForceDownload => false; 17 | Stream? IFile.Content => new MemoryStream(Content); 18 | 19 | public static implicit operator File(FileParameter param) => new File 20 | { 21 | Content = ((IFile)param).Content, 22 | ContentType = param.ContentType, 23 | Length = param.Length, 24 | Name = param.Name, 25 | }; 26 | 27 | public static implicit operator FileParameter(File param) => new FileParameter 28 | { 29 | Content = param.Content?.ReadAllBytes() ?? [], 30 | ContentType = param.ContentType, 31 | Name = param.Name, 32 | }; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Models/IFile.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace IntelliTect.Coalesce.Models 4 | { 5 | /// 6 | /// Representation of a file for use in Coalesce method parameters and returns. 7 | /// 8 | public interface IFile 9 | { 10 | Stream? Content { get; } 11 | string? ContentType { get; } 12 | string? Name { get; } 13 | long Length { get; } 14 | bool ForceDownload { get; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Models/ValidationIssue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace IntelliTect.Coalesce.Models 4 | { 5 | public class ValidationIssue 6 | { 7 | public string Property { get; set; } 8 | public string Issue { get; set; } 9 | 10 | public ValidationIssue(string property, string issue) 11 | { 12 | Property = property ?? throw new ArgumentNullException(nameof(property)); 13 | Issue = issue ?? throw new ArgumentNullException(nameof(issue)); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: InternalsVisibleTo("IntelliTect.Coalesce.CodeGeneration")] 6 | [assembly: InternalsVisibleTo("IntelliTect.Coalesce.AuditLogging")] 7 | [assembly: InternalsVisibleTo("IntelliTect.Coalesce.Tests")] 8 | [assembly: InternalsVisibleTo("IntelliTect.Coalesce.CodeGeneration.Tests")] -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/TypeDefinition/EnumMember.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace IntelliTect.Coalesce.TypeDefinition 6 | { 7 | public class EnumMember 8 | { 9 | public EnumMember( 10 | string name, 11 | object value, 12 | string displayName, 13 | string? description, 14 | string? comment = null 15 | ) 16 | { 17 | Name = name; 18 | DisplayName = displayName; 19 | Value = value; 20 | Description = description; 21 | Comment = comment; 22 | } 23 | 24 | public string Name { get; } 25 | public object Value { get; } 26 | public string DisplayName { get; } 27 | public string? Description { get; } 28 | public string? Comment { get; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/TypeDefinition/Enums/PropertyRole.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace IntelliTect.Coalesce.TypeDefinition.Enums 6 | { 7 | [Flags] 8 | public enum PropertyRole 9 | { 10 | Value, 11 | ReferenceNavigation, 12 | CollectionNavigation, 13 | PrimaryKey, 14 | ForeignKey, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/TypeDefinition/MethodReturnViewModel.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | #nullable enable 6 | 7 | namespace IntelliTect.Coalesce.TypeDefinition 8 | { 9 | /// 10 | /// Shim of a method's return value so it can be treated as an IValueViewModel. 11 | /// 12 | public class MethodReturnViewModel : ValueViewModel 13 | { 14 | internal MethodReturnViewModel(MethodViewModel method) : base(method.ResultType) 15 | { 16 | Method = method; 17 | } 18 | 19 | public override string Name => "$return"; 20 | 21 | public override string DisplayName => "Result"; // TODO: i18n 22 | 23 | public override string? Description => null; 24 | 25 | public MethodViewModel Method { get; } 26 | 27 | public override bool IsRequired => false; 28 | 29 | public override IEnumerable> GetAttributes() 30 | => Method.GetAttributes(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/TypeDefinition/Security/MethodSecurityInfo.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.DataAnnotations; 2 | using IntelliTect.Coalesce.Helpers; 3 | using IntelliTect.Coalesce.TypeDefinition; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Security.Claims; 8 | 9 | namespace IntelliTect.Coalesce.TypeDefinition 10 | { 11 | public class MethodSecurityInfo 12 | { 13 | public MethodSecurityInfo(SecurityPermission execute) 14 | { 15 | Execute = execute; 16 | } 17 | 18 | public SecurityPermission Execute { get; } 19 | 20 | public bool IsExecuteAllowed(ClaimsPrincipal? user) => Execute.IsAllowed(user); 21 | 22 | public override string ToString() => $"Execute: {Execute}"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/TypeDefinition/TypeDiscriminator.cs: -------------------------------------------------------------------------------- 1 | namespace IntelliTect.Coalesce.TypeDefinition 2 | { 3 | public enum TypeDiscriminator 4 | { 5 | Unknown, 6 | Void, 7 | Number, 8 | String, 9 | Boolean, 10 | Date, 11 | Enum, 12 | Model, 13 | Object, 14 | Collection, 15 | File, 16 | Binary 17 | } 18 | 19 | public static class TypeDiscriminatorExtensions 20 | { 21 | public static bool IsCustomType(this TypeDiscriminator type) 22 | { 23 | return type == TypeDiscriminator.Enum || type.IsClassType(); 24 | } 25 | 26 | public static bool IsClassType(this TypeDiscriminator type) 27 | { 28 | return type == TypeDiscriminator.Model || type == TypeDiscriminator.Object; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/TypeUsage/EntityTypeUsage.cs: -------------------------------------------------------------------------------- 1 | using IntelliTect.Coalesce.TypeDefinition; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace IntelliTect.Coalesce.TypeUsage 7 | { 8 | public class EntityTypeUsage 9 | { 10 | public EntityTypeUsage(DbContextTypeUsage context, TypeViewModel typeViewModel, string contextPropertyName) 11 | { 12 | Context = context; 13 | TypeViewModel = typeViewModel; 14 | ClassViewModel = typeViewModel.ClassViewModel ?? throw new ArgumentException("Entity is not a class", nameof(typeViewModel)); 15 | ContextPropertyName = contextPropertyName; 16 | } 17 | 18 | public DbContextTypeUsage Context { get; } 19 | 20 | public TypeViewModel TypeViewModel { get; } 21 | 22 | public ClassViewModel ClassViewModel { get; } 23 | 24 | public string ContextPropertyName { get; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Utilities/EntityFrameworkServiceProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Infrastructure; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using System; 5 | using System.Linq; 6 | 7 | namespace IntelliTect.Coalesce.Utilities; 8 | 9 | internal class EntityFrameworkServiceProvider(DbContext db) : IServiceProvider 10 | { 11 | public object? GetService(Type serviceType) 12 | { 13 | // From Microsoft.EntityFrameworkCore.Infrastructure.Internal.InfrastructureExtensions; 14 | // Copied directly to avoid having to dynamically create generic overloads at runtime, 15 | // and also because we don't want to throw for services not found - we want to return null. 16 | 17 | var internalServiceProvider = db.GetInfrastructure(); 18 | 19 | return internalServiceProvider.GetService(serviceType) 20 | ?? internalServiceProvider.GetService() 21 | ?.Extensions.OfType().FirstOrDefault() 22 | ?.ApplicationServiceProvider 23 | ?.GetService(serviceType); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Utilities/LazyValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace IntelliTect.Coalesce.Utilities 4 | { 5 | internal struct LazyValue 6 | { 7 | bool HasValue; 8 | T Value; 9 | 10 | public T GetValue(Func getter) 11 | { 12 | if (HasValue) return Value; 13 | Value = getter(); 14 | HasValue = true; 15 | return Value; 16 | } 17 | 18 | public void Reset() 19 | { 20 | HasValue = false; 21 | Value = default!; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Utilities/ReadOnlyHashSet.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace IntelliTect.Coalesce.Utilities 7 | { 8 | public readonly struct ReadOnlyHashSet : IReadOnlyCollection 9 | where T : notnull 10 | { 11 | private readonly ConcurrentHashSet set; 12 | 13 | internal ReadOnlyHashSet(ConcurrentHashSet set) 14 | { 15 | this.set = set; 16 | } 17 | 18 | public int Count => set.Count; 19 | 20 | public IEnumerator GetEnumerator() => set.GetEnumerator(); 21 | 22 | IEnumerator IEnumerable.GetEnumerator() => set.GetEnumerator(); 23 | 24 | public bool Contains(T? item) => set.Contains(item); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/IntelliTect.Coalesce/Validation/ValidationResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace IntelliTect.Coalesce.Validation 7 | { 8 | internal class ValidationResult 9 | { 10 | public bool WasSuccessful { get; set; } 11 | public bool IsWarning { get; set; } 12 | public string? Area { get; set; } 13 | public string? Message { get; set; } 14 | 15 | public bool IsError => !WasSuccessful && !IsWarning; 16 | 17 | public override string ToString() 18 | { 19 | if (WasSuccessful) 20 | { 21 | return $" Success: {Area}: {Message}"; 22 | } 23 | if (IsWarning) 24 | { 25 | return $"-- Warning: {Area}: {Message}"; 26 | } 27 | return $"** Failure: {Area}: {Message}"; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify2/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not ie <= 11 4 | not dead -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify2/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.ts,*.vue,*.scss] 2 | indent_style = space 3 | indent_size = 2 -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify2/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | extends: [ 7 | "plugin:vue/essential", 8 | "eslint:recommended", 9 | "@vue/eslint-config-typescript", 10 | ], 11 | rules: { 12 | "no-console": process.env.NODE_ENV === "production" ? "error" : "off", 13 | "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off", 14 | "@typescript-eslint/no-unused-vars": "error", 15 | "no-unused-vars": "off", 16 | "no-case-declarations": "off", 17 | "no-undef": "off", // Redundant with Typescript 18 | }, 19 | ignorePatterns: ["lib", "dist", "node_modules"], 20 | }; 21 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify2/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | #build outputs 5 | !/dist 6 | /dist/* 7 | !/dist/cjs 8 | /dist/cjs/* 9 | !/dist/cjs/package.json 10 | 11 | /lib/* 12 | !/lib/cjs 13 | /lib/cjs/* 14 | !/lib/cjs/package.json 15 | 16 | # local env files 17 | .env.local 18 | .env.*.local 19 | 20 | # Log files 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # Editor directories and files 26 | .idea 27 | .vscode 28 | *.suo 29 | *.ntvs* 30 | *.njsproj 31 | *.sln 32 | *.sw? 33 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify2/README.md: -------------------------------------------------------------------------------- 1 | # coalesce-vue-vuetify2 2 | 3 | Component library for [Coalesce](https://github.com/IntelliTect/Coalesce), using Vue 2 and Vuetify 2. 4 | 5 | Learn more about Coalesce [On GitHub](https://github.com/IntelliTect/Coalesce), or [read the documentation](https://intellitect.github.io/Coalesce/stacks/vue/coalesce-vue-vuetify/overview.html). 6 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify2/dist/cjs/package.json: -------------------------------------------------------------------------------- 1 | {"type": "commonjs"} -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify2/lib/cjs/package.json: -------------------------------------------------------------------------------- 1 | {"type": "commonjs"} -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify2/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelliTect/Coalesce/bbbb55fd51a58355ab296730e7b0f40f4f543ca6/src/coalesce-vue-vuetify2/public/favicon.ico -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify2/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | coalesce-vue-vuetify 9 | 10 | 11 | 12 | 13 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify2/src/build.ts: -------------------------------------------------------------------------------- 1 | /** Component name resolver for unplugin-vue-components. */ 2 | 3 | let moduleName: Promise; 4 | export function CoalesceVuetifyResolver() { 5 | moduleName = (async () => { 6 | // See if coalesce-vue-vuetify2 was aliased in package.json as coalesce-vue-vuetify. 7 | // We have to do so in a way that will work in both ESM and CJS. 8 | 9 | if ("require" in global) { 10 | try { 11 | require.resolve("coalesce-vue-vuetify"); 12 | return "coalesce-vue-vuetify"; 13 | } catch { 14 | /* not cjs */ 15 | } 16 | } else { 17 | try { 18 | if (await import.meta.resolve?.("coalesce-vue-vuetify")) 19 | return "coalesce-vue-vuetify"; 20 | } catch { 21 | /* not esm */ 22 | } 23 | } 24 | return "coalesce-vue-vuetify2"; 25 | })(); 26 | 27 | return { 28 | type: "component", 29 | resolve: async (name: string) => { 30 | if (name.match(/^C[A-Z]/)) 31 | return { name, from: (await moduleName) + "/lib" }; 32 | }, 33 | } as const; 34 | } 35 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify2/src/components/input/c-input-props-provider.vue: -------------------------------------------------------------------------------- 1 | 27 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify2/src/components/input/c-list-pagination.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 30 | 31 | 39 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify2/src/index.dist.ts: -------------------------------------------------------------------------------- 1 | /** index.dist.ts is a non-treeshakable version of coalesce-vue-vuetify, 2 | * intended for use with the non-treeshakable version of vuetify (e.g. non `/lib` import.) */ 3 | 4 | import { PluginObject } from "vue"; 5 | 6 | import BasePlugin, { CoalesceVuetifyOptions } from "./index"; 7 | import * as components from "./components"; 8 | 9 | const Plugin = >{ 10 | install(Vue, options) { 11 | Vue.use(BasePlugin, options); 12 | 13 | for (const key in components) { 14 | const component = (components as any)[key]; 15 | Vue.component(key, component as typeof Vue); 16 | } 17 | }, 18 | }; 19 | 20 | import "./shared.scss"; 21 | export * from "./components"; 22 | export * from "./util"; 23 | export default Plugin; 24 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify2/src/index.ts: -------------------------------------------------------------------------------- 1 | /** index.ts is a treeshakable version of coalesce-vue-vuetify, 2 | * intended for use with the treeshakable version of vuetify (e.g. `vuetify/lib` import). 3 | * It does not register its own components globally, requiring use of techniques like 4 | * unplugin-vue-components (or tedious manual imports, or manual global imports) in consuming projects. 5 | * 6 | */ 7 | 8 | import type { PluginObject } from "vue"; 9 | import type { Domain } from "coalesce-vue"; 10 | 11 | export interface CoalesceVuetifyOptions { 12 | /** A reference to the whole set of Coalesce-generated metadata for the application, 13 | * as exported from `metadata.g.ts`, e.g. `import metadata from '@/metadata.g'`. */ 14 | readonly metadata?: Domain; 15 | } 16 | 17 | declare module "vue/types/vue" { 18 | export interface Vue { 19 | readonly $coalesce: CoalesceVuetifyOptions; 20 | } 21 | } 22 | 23 | const Plugin = >{ 24 | install(Vue, options) { 25 | Vue.prototype.$coalesce = options ?? {}; 26 | }, 27 | }; 28 | 29 | import "./shared.scss"; 30 | export * from "./components"; 31 | export * from "./util"; 32 | export default Plugin; 33 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify2/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue' 3 | export default Vue 4 | } 5 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify2/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "paths": { 6 | "coalesce-vue": ["../coalesce-vue/src"], 7 | "coalesce-vue/*": ["../coalesce-vue/src/*"] 8 | } 9 | }, 10 | "include": ["src/**/*"] 11 | } 12 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify2/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "experimentalDecorators": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "sourceMap": true, 13 | "baseUrl": ".", 14 | "paths": { 15 | "@/*": ["src/*"], 16 | "coalesce-vue": ["../coalesce-vue/src"], 17 | "coalesce-vue/*": ["../coalesce-vue/src/*"], 18 | "vue": ["node_modules/vue"], 19 | "vue/*": ["node_modules/vue/*"] 20 | }, 21 | "lib": ["esnext", "dom", "dom.iterable", "scripthost"], 22 | "types": ["node", "vuetify", "vue-router"] 23 | }, 24 | "include": [ 25 | "rollup.config.ts", 26 | "src/**/*.ts", 27 | "src/**/*.tsx", 28 | "src/**/*.vue", 29 | "tests/**/*.ts", 30 | "tests/**/*.tsx" 31 | ], 32 | "exclude": ["node_modules"] 33 | } 34 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify3/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.ts,*.tsx,*.vue,*.scss] 2 | indent_style = space 3 | indent_size = 2 -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify3/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | extends: [ 7 | "plugin:vue/vue3-essential", 8 | "eslint:recommended", 9 | "@vue/eslint-config-typescript", 10 | ], 11 | parserOptions: { 12 | ecmaVersion: "latest", 13 | }, 14 | rules: { 15 | "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off", 16 | "no-useless-escape": "off", 17 | "no-case-declarations": "off", 18 | "no-console": "off", 19 | "vue/no-mutating-props": "off", // Falsely reports for mutating children of props 20 | "no-undef": "off", // Redundant with Typescript 21 | }, 22 | ignorePatterns: ["dist", "node_modules"], 23 | }; 24 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify3/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | #build outputs 5 | !/dist 6 | /dist/* 7 | !/dist/cjs 8 | /dist/cjs/* 9 | !/dist/cjs/package.json 10 | 11 | /lib/* 12 | !/lib/cjs 13 | /lib/cjs/* 14 | !/lib/cjs/package.json 15 | 16 | # local env files 17 | .env.local 18 | .env.*.local 19 | 20 | # Log files 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # Editor directories and files 26 | .idea 27 | .vscode 28 | *.suo 29 | *.ntvs* 30 | *.njsproj 31 | *.sln 32 | *.sw? 33 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify3/README.md: -------------------------------------------------------------------------------- 1 | # coalesce-vue-vuetify3 2 | 3 | Component library for [Coalesce](https://github.com/IntelliTect/Coalesce), using Vue 3 and Vuetify 3. 4 | 5 | Learn more about Coalesce [On GitHub](https://github.com/IntelliTect/Coalesce), or [read the documentation](https://intellitect.github.io/Coalesce/stacks/vue/coalesce-vue-vuetify/overview.html). 6 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify3/src/build.ts: -------------------------------------------------------------------------------- 1 | import { existsSync, readFileSync } from "node:fs"; 2 | 3 | /** Component name resolver for unplugin-vue-components that resolves coalesce-vue-vuetify3 components. */ 4 | export function CoalesceVuetifyResolver() { 5 | // Read the actual component names from the source code 6 | // so that we can never have a false match. 7 | const componentExports = ["./components/index.d.ts", "./components/index.ts"] 8 | .map((f) => new URL(f, import.meta.url)) 9 | .filter(existsSync) 10 | .map((f) => readFileSync(f, { encoding: "utf-8" }))[0]; 11 | 12 | const components = new Set(componentExports.match(/C[A-Z][A-Za-z]+/g)); 13 | 14 | return { 15 | type: "component", 16 | resolve: (name: string) => { 17 | if (components.has(name)) return { name, from: "coalesce-vue-vuetify3" }; 18 | }, 19 | } as const; 20 | } 21 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify3/src/components/admin/c-admin-editor.spec.tsx: -------------------------------------------------------------------------------- 1 | import { Person } from "@test-targets/models.g"; 2 | import { CAdminEditor } from ".."; 3 | import { 4 | PersonViewModel, 5 | PersonListViewModel, 6 | } from "@test-targets/viewmodels.g"; 7 | 8 | describe("CAdminEditor", () => { 9 | test("types", () => { 10 | const model = new Person(); 11 | const vm = new PersonViewModel(); 12 | const list = new PersonListViewModel(); 13 | 14 | () => ; 15 | //@ts-expect-error plain model not allowed 16 | () => ; 17 | //@ts-expect-error list not allowed 18 | () => ; 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify3/src/components/admin/c-admin-methods.spec.tsx: -------------------------------------------------------------------------------- 1 | import { CAdminMethods } from ".."; 2 | import { 3 | PersonViewModel, 4 | PersonListViewModel, 5 | WeatherServiceViewModel, 6 | } from "@test-targets/viewmodels.g"; 7 | 8 | describe("CAdminMethods", () => { 9 | test("types", () => { 10 | const vm = new PersonViewModel(); 11 | const listVm = new PersonListViewModel(); 12 | const serviceVm = new WeatherServiceViewModel(); 13 | 14 | const rename = vm.$metadata.methods.rename; 15 | 16 | () => ; 17 | () => ; 18 | () => ; 19 | 20 | // @ts-expect-error not a model 21 | () => ; 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify3/src/components/input/c-input.spec.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 25 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify3/src/components/input/c-list-pagination.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 30 | 31 | 39 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify3/src/components/input/c-select-many-to-many.spec.tsx: -------------------------------------------------------------------------------- 1 | import { CSelectManyToMany } from ".."; 2 | import { CaseViewModel } from "@test-targets/viewmodels.g"; 3 | 4 | describe("CSelectManyToMany", () => { 5 | // prettier-ignore 6 | test("types", () => { 7 | const caseVm = new CaseViewModel(); 8 | 9 | () => ; 10 | () => ( 11 | 15 | ); 16 | 17 | //@ts-expect-error wrong type 18 | () => ; 19 | () => ( 20 | //@ts-expect-error wrong type 21 | 22 | ); 23 | 24 | //@ts-expect-error missing `model` 25 | () => ; 26 | //@ts-expect-error missing `for` 27 | () => ; 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify3/src/composables/useMetadata.ts: -------------------------------------------------------------------------------- 1 | import { inject } from "vue"; 2 | import { Domain } from "coalesce-vue"; 3 | 4 | export const metadataKey = Symbol("coalesce metadata"); 5 | 6 | export function useMetadata() { 7 | return inject(metadataKey) as Domain; 8 | } 9 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify3/src/env.d.ts: -------------------------------------------------------------------------------- 1 | // /// 2 | 3 | declare module "*.vue" { 4 | import type { DefineComponent } from "vue"; 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 6 | const component: DefineComponent<{}, {}, any>; 7 | export default component; 8 | } 9 | 10 | // declare module 'vuetify' 11 | // declare module 'vuetify/lib/components' 12 | // declare module 'vuetify/lib/directives' 13 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify3/src/index.ts: -------------------------------------------------------------------------------- 1 | import "./shared.scss"; 2 | export * from "./components"; 3 | export * from "./util"; 4 | export * from "./install"; 5 | export { useMetadata } from "./composables/useMetadata"; 6 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify3/tsconfig.build-types.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "declaration": true, 6 | "emitDeclarationOnly": true, 7 | 8 | // Type checking is done according to tsconfig.json, 9 | // which is configured to be able to use coalesce-vue internal types. 10 | 11 | // However, we must emit the types using the build copy of coalesce-vue 12 | // so that typescript doesn't also emit types for coalesce-vue. 13 | // We have to turn off typechecking to do this because 14 | // the @internal types in coalesce-vue aren't available there. 15 | "noCheck": true, 16 | 17 | "rootDir": "./src", 18 | "paths": { 19 | "coalesce-vue": ["../coalesce-vue/lib"], 20 | "coalesce-vue/*": ["../coalesce-vue/lib/*"], 21 | "coalesce-vue/lib/*": ["../coalesce-vue/lib/*"] 22 | } 23 | }, 24 | "include": ["src/index.ts"] 25 | } 26 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify3/tsconfig.build-utils.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "NodeNext", 4 | "target": "ESNext", 5 | "outDir": "./dist", 6 | "declaration": true, 7 | "skipLibCheck": true, 8 | "moduleResolution": "NodeNext" 9 | }, 10 | "include": ["src/build.ts"], 11 | "exclude": ["node_modules"] 12 | } 13 | -------------------------------------------------------------------------------- /src/coalesce-vue-vuetify3/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2021", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "jsxImportSource": "vue", 8 | "importHelpers": true, 9 | "moduleResolution": "node", 10 | "sourceMap": true, 11 | "skipLibCheck": true, 12 | "resolveJsonModule": true, 13 | "allowSyntheticDefaultImports": true, 14 | "baseUrl": ".", 15 | "paths": { 16 | "@test/*": ["test/*"], 17 | "@test-targets/*": ["../test-targets/*"], 18 | "coalesce-vue": ["../coalesce-vue/src"], 19 | "coalesce-vue/*": ["../coalesce-vue/src/*"], 20 | "coalesce-vue/lib/*": ["../coalesce-vue/src/*"], 21 | "vue": ["node_modules/vue"], 22 | "vue/*": ["node_modules/vue/*"], 23 | "vue-router": ["node_modules/vue-router"] 24 | }, 25 | "lib": ["esnext", "dom", "dom.iterable", "scripthost"], 26 | "types": ["node", "vue-router", "vuetify", "vitest/globals"] 27 | }, 28 | "include": [ 29 | "vite.config.ts", 30 | "src/**/*.ts", 31 | "src/**/*.tsx", 32 | "src/**/*.vue", 33 | "test/**/*.ts", 34 | "test/**/*.tsx" 35 | ], 36 | "exclude": ["node_modules"] 37 | } 38 | -------------------------------------------------------------------------------- /src/coalesce-vue/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{vue,ts,js}] 2 | indent_style = space 3 | indent_size = 2 4 | 5 | 6 | [*.cs] 7 | -------------------------------------------------------------------------------- /src/coalesce-vue/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.tgz 3 | lib/**/*.js 4 | lib/**/*.ts 5 | *.results.xml -------------------------------------------------------------------------------- /src/coalesce-vue/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | // Use IntelliSense to learn about possible Node.js debug attributes. 4 | // Hover to view descriptions of existing attributes. 5 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "type": "pwa-node", 10 | "request": "launch", 11 | "name": "Debug Current Test File", 12 | "autoAttachChildProcesses": true, 13 | "skipFiles": ["/**"], 14 | "program": "${workspaceRoot}/node_modules/vitest/vitest.mjs", 15 | "args": ["run", "${relativeFile}"], 16 | "smartStep": true, 17 | "console": "integratedTerminal", 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /src/coalesce-vue/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnPaste": false, 3 | "editor.formatOnSave": true, 4 | "editor.formatOnType": true, 5 | "editor.defaultFormatter": "esbenp.prettier-vscode", 6 | "prettier.useEditorConfig": false, 7 | "typescript.tsdk": "node_modules\\typescript\\lib", 8 | "[typescript]": { 9 | "editor.insertSpaces": true, 10 | "editor.tabSize": 2, 11 | "editor.autoIndent": "advanced" 12 | }, 13 | "workbench.colorCustomizations": { 14 | "titleBar.activeBackground": "#a82e1f", 15 | "titleBar.inactiveBackground": "#a82e1f99", 16 | "titleBar.activeForeground": "#e7e7e7", 17 | "titleBar.inactiveForeground": "#e7e7e799" 18 | }, 19 | "peacock.color": "#d33a27" 20 | } -------------------------------------------------------------------------------- /src/coalesce-vue/README.md: -------------------------------------------------------------------------------- 1 | # coalesce-vue 2 | 3 | Core Vue library for [Coalesce](https://github.com/IntelliTect/Coalesce), supporting both Vue 2 and Vue 3. 4 | 5 | Learn more about Coalesce [On GitHub](https://github.com/IntelliTect/Coalesce), or [read the documentation](https://intellitect.github.io/Coalesce/stacks/vue/overview.html). 6 | -------------------------------------------------------------------------------- /src/coalesce-vue/lib/cjs/package.json: -------------------------------------------------------------------------------- 1 | {"type": "commonjs"} -------------------------------------------------------------------------------- /src/coalesce-vue/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./metadata.js"; 2 | export * from "./model.js"; 3 | export * from "./api-client.js"; 4 | export * from "./viewmodel.js"; 5 | export * from "./util.js"; 6 | -------------------------------------------------------------------------------- /src/coalesce-vue/test/global-setup.ts: -------------------------------------------------------------------------------- 1 | // Importing @vue/test-utils will turn off the Vue devtools and productionTip warnings. 2 | // Normally we could do this ourselves, but in order for this project to be a vue2/vue3 polyglot, 3 | // we have to check that we're running vue2 before we try to turn these flags off. 4 | // If we use `IsVue2` from our own util.ts file, this then makes there be a second async import 5 | // after we import from 'vue', which gives the setTimeout(..., 0) function in Vue that prints 6 | // these warnings time to run before we're able to actually turn the warnings off. 7 | // So, to make things easy, we just let the default functionality in @vue/test-utils turn them off. 8 | import "@vue/test-utils"; 9 | 10 | import { version } from "vue"; 11 | 12 | declare const TEST_EXPECTED_VUE_VERSION: number; 13 | 14 | expect(TEST_EXPECTED_VUE_VERSION).toBe(+version.charAt(0)); 15 | -------------------------------------------------------------------------------- /src/coalesce-vue/test/utils.spec.vue2.ts: -------------------------------------------------------------------------------- 1 | import { bindToQueryString } from "../src"; 2 | import Vue from "vue"; 3 | 4 | describe("VueInstance", () => { 5 | test("is assignable from Vue class component", async () => { 6 | class MyComponent extends Vue { 7 | created() { 8 | bindToQueryString(this, { a: 1 }, "a"); 9 | } 10 | } 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/coalesce-vue/tsconfig.build.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./lib/cjs", 5 | "module": "commonjs", 6 | "target": "es2018", 7 | "verbatimModuleSyntax": false 8 | }, 9 | "include": ["src/**/*"] 10 | } 11 | -------------------------------------------------------------------------------- /src/coalesce-vue/tsconfig.build.esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./lib", 5 | "module": "es2022", 6 | "target": "es2022" 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /src/coalesce-vue/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true, 4 | "emitDecoratorMetadata": true, 5 | "importHelpers": true, 6 | "downlevelIteration": true, 7 | "verbatimModuleSyntax": true, 8 | 9 | "noImplicitOverride": true, 10 | "strict": true, 11 | "skipLibCheck": true, 12 | "stripInternal": true, 13 | 14 | "module": "esnext", 15 | "moduleResolution": "node", 16 | "target": "ES2022", 17 | "declaration": true, 18 | "lib": ["es2015", "es2017", "dom", "ES2021.WeakRef"], 19 | "types": ["vitest/globals", "vue-router"], 20 | 21 | "paths": { 22 | "@test-targets/*": ["../test-targets/*"], 23 | "coalesce-vue/lib/*": ["./src/*"] 24 | } 25 | }, 26 | 27 | "exclude": ["node_modules"], 28 | "include": ["src/**/*", "test/**/*"] 29 | } 30 | -------------------------------------------------------------------------------- /src/coalesce-vue/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, UserConfig } from "vitest/config"; 2 | import path from "path"; 3 | 4 | export default defineConfig({ 5 | define: { 6 | TEST_EXPECTED_VUE_VERSION: 2, 7 | }, 8 | test: { 9 | globals: true, 10 | environment: "jsdom", 11 | setupFiles: ["test/global-setup.ts"], 12 | include: ["**/*.{test,spec}.{ts,js}", "**/*.{test,spec}.vue2.{ts,js}"], 13 | }, 14 | resolve: { 15 | alias: [ 16 | // Imports inside the generated test targets: 17 | { find: "coalesce-vue/lib", replacement: path.resolve(__dirname, "src") }, 18 | { 19 | find: "@test-targets", 20 | replacement: path.resolve(__dirname, "../test-targets"), 21 | }, 22 | ], 23 | }, 24 | }) as UserConfig; 25 | -------------------------------------------------------------------------------- /src/coalesce-vue/vue3-tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "coalesce-vue3", 3 | "version": "0.1.0-local", 4 | "description": "Development only package for coalesce-vue for Vue3. Runs tests for vue3 against the polyglot package coalesce-vue. This exists because we have to install conflicting versions of vue, vue-router, and test-utils. NPM still enforces peerDependencies even when using aliased package names, which prevents us from installing them as aliases in the parent project.", 5 | "type": "module", 6 | "private": true, 7 | "scripts": { 8 | "build": "tsc", 9 | "test": "vitest", 10 | "coverage": "vitest --coverage" 11 | }, 12 | "devDependencies": { 13 | "vue": "3.4.19", 14 | "vue-router": "4.1.6", 15 | "@vue/test-utils": "2.3.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/coalesce-vue/vue3-tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | // We only want to do TS builds for validation of type compatibility with vue3, not for emitting. 5 | "noEmit": true, 6 | "paths": { 7 | "@vue/test-utils": ["./node_modules/@vue/test-utils"], 8 | "vue": ["./node_modules/vue"], 9 | "vue-router": ["./node_modules/vue-router"], 10 | 11 | // For test-targets files: 12 | "@test-targets/*": ["../../test-targets/*"], 13 | "coalesce-vue/lib/*": ["../src/*"] 14 | } 15 | }, 16 | 17 | "include": ["../src/**/*", "../test/**/*"], 18 | "exclude": ["../test/**/*.spec.vue2.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | Test.Template.Instance/ 7 | bin/ 8 | obj/ 9 | *.nupkg -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dotnet.defaultSolution": "content\\Coalesce.Starter.Vue.sln", 3 | "editor.formatOnSave": true, 4 | } -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/Directory.Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | 5 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/README.md: -------------------------------------------------------------------------------- 1 | # Coalesce Vue Template 2 | 3 | 4 | This template will set up a new Coalesce Vue solution which you can build your app upon. 5 | 6 | For instructions and additional information, visit [the Coalesce Documentation](https://intellitect.github.io/Coalesce/stacks/vue/getting-started.html). 7 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{vue,ts,tsx,csproj,props,json,html,scss}] 4 | indent_style = space 5 | indent_size = 2 6 | 7 | [*.cs] 8 | indent_style = space 9 | indent_size = 4 10 | csharp_style_namespace_declarations=file_scoped:suggestion -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/.gitattributes: -------------------------------------------------------------------------------- 1 | *.g.cs linguist-generated=true 2 | *.g.ts linguist-generated=true 3 | auto-imports.d.ts linguist-generated=true 4 | components.d.ts linguist-generated=true -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": [ 7 | "streetsidesoftware.code-spell-checker", 8 | "dbaeumer.vscode-eslint", 9 | "vue.volar", 10 | "esbenp.prettier-vscode", 11 | "antfu.goto-alias" 12 | ], 13 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 14 | "unwantedRecommendations": [] 15 | } 16 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "Coalesce.Starter.Vue.Web\\node_modules\\typescript\\lib", 3 | "dotnet.defaultSolution": "Coalesce.Starter.Vue.sln", 4 | "editor.formatOnSave": true, 5 | "editor.codeActionsOnSave": { 6 | "source.fixAll.eslint": "explicit" 7 | }, 8 | "cSpell.words": ["Vite", "unplugin", "iconsets", "viewmodels", "composables"] 9 | } 10 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data.Test/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | namespace Coalesce.Starter.Vue.Data.Test; 2 | 3 | public class UnitTest1 : TestBase 4 | { 5 | [Fact] 6 | public void Test1() 7 | { 8 | #if ExampleModel 9 | // Arrange 10 | var widget1 = new Widget { Name = "Gnoam Sprecklesprocket", Category = WidgetCategory.Sprecklesprockets }; 11 | Db.Add(widget1); 12 | Db.SaveChanges(); 13 | 14 | RefreshServices(); 15 | 16 | // Act 17 | var widget2 = Db.Widgets.Single(); 18 | 19 | // Assert 20 | Assert.Equal(WidgetCategory.Sprecklesprockets, widget2.Category); 21 | 22 | // After calling RefreshServices, we have a different DbContext instance 23 | // and so we'll get a different entity instance. 24 | Assert.NotEqual(widget1, widget2); 25 | #endif 26 | } 27 | } -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data/Auth/AppClaimTypes.cs: -------------------------------------------------------------------------------- 1 | namespace Coalesce.Starter.Vue.Data; 2 | 3 | public static class AppClaimTypes 4 | { 5 | public const string Role = "role"; 6 | public const string Permission = "perm"; 7 | public const string UserId = "sub"; 8 | public const string UserName = "username"; 9 | public const string Email = "email"; 10 | public const string FullName = "name"; 11 | #if Tenancy 12 | public const string TenantId = "tid"; 13 | #endif 14 | } 15 | 16 | #if Tenancy 17 | public static class AppClaimValues 18 | { 19 | public const string GlobalAdminRole = "GlobalAdmin"; 20 | public const string NullTenantId = "00000000-0000-0000-0000-000000000000"; 21 | } 22 | #endif -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data/Auth/DbContextFactoryExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Coalesce.Starter.Vue.Data.Auth; 2 | 3 | public static class DbContextFactoryExtensions 4 | { 5 | public static IEnumerable GetTenantIds(this IDbContextFactory factory) 6 | { 7 | using var db = factory.CreateDbContext(); 8 | 9 | return db.Tenants.Select(t => t.TenantId).ToList(); 10 | } 11 | 12 | public static AppDbContext CreateDbContext(this IDbContextFactory factory, string tenantId) 13 | { 14 | var db = factory.CreateDbContext(); 15 | db.TenantId = tenantId; 16 | return db; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data/Auth/Permission.cs: -------------------------------------------------------------------------------- 1 | namespace Coalesce.Starter.Vue.Data; 2 | 3 | /// 4 | /// The permissions available for assignment to s. 5 | /// Permissions generally describe actions that a user can take within the application, 6 | /// while roles are usually representative of a job title or function. 7 | /// 8 | public enum Permission 9 | { 10 | // Note: Enum values/numbers are not used for persistance - roles are stored as strings in the database. 11 | 12 | [Display(Name = "Admin - General", Description = "Modify application configuration and other administrative functions excluding user/role management.")] 13 | Admin = 1, 14 | 15 | [Display(Name = "Admin - Users", Description = "Add and modify users accounts and their assigned roles. Edit roles and their permissions.")] 16 | UserAdmin, 17 | 18 | ViewAuditLogs 19 | } 20 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data/Auth/UserInfo.cs: -------------------------------------------------------------------------------- 1 | namespace Coalesce.Starter.Vue.Data.Auth; 2 | 3 | public class UserInfo 4 | { 5 | public string? Id { get; set; } 6 | 7 | public string? UserName { get; set; } 8 | 9 | #if Identity 10 | public string? Email { get; set; } 11 | public string? FullName { get; set; } 12 | 13 | public required ICollection Roles { get; set; } 14 | public required ICollection Permissions { get; set; } 15 | #endif 16 | #if Tenancy 17 | [MaxLength(36)] 18 | public string? TenantId { get; set; } 19 | public string? TenantName { get; set; } 20 | #endif 21 | } -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data/Auth/UserInvitation.cs: -------------------------------------------------------------------------------- 1 | namespace Coalesce.Starter.Vue.Data.Auth; 2 | 3 | public class UserInvitation 4 | { 5 | public required string TenantId { get; set; } 6 | 7 | public required string Email { get; set; } 8 | 9 | public required DateTimeOffset Issued { get; set; } 10 | 11 | public required string[] Roles { get; set; } 12 | } 13 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data/Coalesce/AppBehaviors.cs: -------------------------------------------------------------------------------- 1 | namespace Coalesce.Starter.Vue.Data.Coalesce; 2 | 3 | public abstract class AppBehaviors : StandardBehaviors 4 | where T : class 5 | { 6 | protected AppBehaviors(CrudContext context) : base(context) 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data/Coalesce/AppDataSource.cs: -------------------------------------------------------------------------------- 1 | namespace Coalesce.Starter.Vue.Data.Coalesce; 2 | 3 | public abstract class AppDataSource : StandardDataSource 4 | where T : class 5 | { 6 | protected AppDataSource(CrudContext context) : base(context) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data/Communication/AzureEmailOptions.cs: -------------------------------------------------------------------------------- 1 | using Azure.Identity; 2 | 3 | namespace Coalesce.Starter.Vue.Data.Communication; 4 | 5 | public class AzureEmailOptions 6 | { 7 | /// 8 | /// The ACS resource endpoint, e.g. "https://my-acs-resource.unitedstates.communication.azure.com". 9 | /// This code is configured to use managed RBAC authentication via 10 | /// and so does not use a connection string or API keys. Assign the Contributor role to allow email sending. 11 | /// 12 | public string? Endpoint { get; set; } 13 | 14 | public string? SenderEmail { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data/Communication/IEmailService.cs: -------------------------------------------------------------------------------- 1 | namespace Coalesce.Starter.Vue.Data.Communication; 2 | 3 | public interface IEmailService 4 | { 5 | Task SendEmailAsync(string to, string subject, string htmlMessage); 6 | } 7 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data/Communication/NoOpEmailService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Hosting; 2 | 3 | namespace Coalesce.Starter.Vue.Data.Communication; 4 | 5 | public class NoOpEmailService( 6 | IHostEnvironment env 7 | ) : IEmailService 8 | { 9 | public Task SendEmailAsync(string to, string subject, string htmlMessage) 10 | { 11 | if (env.IsProduction()) 12 | { 13 | throw new NotImplementedException("Email sending has not been implemented."); 14 | } 15 | 16 | // When email sending has not been implemented, dump the email content into the result message 17 | // so that essential functions during initial development (e.g. account setup links) 18 | // can still be used. 19 | 20 | return Task.FromResult(new ItemResult(true, 21 | $"DEVEOPMENT ONLY: Email sending is not configured, or is disabled by configuration. " + 22 | $"The following content would have been sent to {to}:\n\n{htmlMessage}\n\n")); 23 | } 24 | } -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data/Communication/SendGridEmailOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Coalesce.Starter.Vue.Data.Communication; 2 | 3 | public class SendGridEmailOptions 4 | { 5 | public string? ApiKey { get; set; } 6 | 7 | public string? SenderEmail { get; set; } 8 | } 9 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data/DevelopmentAppDbContextFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Design; 3 | 4 | namespace Coalesce.Starter.Vue.Data; 5 | 6 | public class DevelopmentAppDbContextFactory : IDesignTimeDbContextFactory 7 | { 8 | public AppDbContext CreateDbContext(string[] args) 9 | { 10 | // This is only used when adding migrations and updating the database from the cmd line. 11 | // It shouldn't ever be used in code where it might end up running in production. 12 | var builder = new DbContextOptionsBuilder(); 13 | builder.UseSqlServer("Server=(localdb)\\MSSQLLocalDB;Database=Coalesce.Starter.Vue;Trusted_Connection=True;TrustServerCertificate=True;"); 14 | return new AppDbContext(builder.Options); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using IntelliTect.Coalesce; 2 | global using IntelliTect.Coalesce.DataAnnotations; 3 | global using IntelliTect.Coalesce.Models; 4 | global using Microsoft.EntityFrameworkCore; 5 | global using Microsoft.Extensions.Logging; 6 | global using System.ComponentModel.DataAnnotations; 7 | global using System.ComponentModel.DataAnnotations.Schema; 8 | global using static IntelliTect.Coalesce.DataAnnotations.SecurityPermissionLevels; 9 | global using System.Security.Claims; 10 | global using Coalesce.Starter.Vue.Data.Auth; 11 | global using Coalesce.Starter.Vue.Data.Coalesce; 12 | global using Coalesce.Starter.Vue.Data.Utilities; 13 | #if (Identity || ExampleModel || TrackingBase) 14 | global using Coalesce.Starter.Vue.Data.Models; 15 | #endif 16 | 17 | global using OrderByDirections = IntelliTect.Coalesce.DataAnnotations.DefaultOrderByAttribute.OrderByDirections; 18 | global using SearchMethods = IntelliTect.Coalesce.DataAnnotations.SearchAttribute.SearchMethods; 19 | global using HttpMethod = IntelliTect.Coalesce.DataAnnotations.HttpMethod; 20 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data/Models/Tenancy/ITenanted.cs: -------------------------------------------------------------------------------- 1 | namespace Coalesce.Starter.Vue.Data.Models; 2 | 3 | /// 4 | /// The model belongs to a tenant and should be filtered to the tenant of the current user/HTTP request. 5 | /// 6 | public interface ITenanted 7 | { 8 | string TenantId { get; set; } 9 | Tenant? Tenant { get; set; } 10 | } -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data/Models/Tenancy/TenantMembership.cs: -------------------------------------------------------------------------------- 1 | namespace Coalesce.Starter.Vue.Data.Models; 2 | 3 | [Index(nameof(UserId), nameof(TenantId), IsUnique = true)] 4 | [InternalUse] 5 | public class TenantMembership : TenantedBase 6 | { 7 | [MaxLength(36)] 8 | public string TenantMembershipId { get; set; } = Guid.NewGuid().ToString(); 9 | 10 | [Required] 11 | public string UserId { get; set; } = default!; 12 | public User? User { get; set; } 13 | } 14 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data/Models/Tenancy/TenantedBase.cs: -------------------------------------------------------------------------------- 1 | namespace Coalesce.Starter.Vue.Data.Models; 2 | 3 | public abstract class TenantedBase 4 | #if TrackingBase 5 | : TrackingBase, ITenanted 6 | #else 7 | : ITenanted 8 | #endif 9 | { 10 | [InternalUse, Required, MaxLength(36)] 11 | public string TenantId { get; set; } = null!; 12 | [InternalUse] 13 | public Tenant? Tenant { get; set; } 14 | } 15 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data/Models/UserPhoto.cs: -------------------------------------------------------------------------------- 1 | namespace Coalesce.Starter.Vue.Data.Models; 2 | 3 | [InternalUse] 4 | [Index(nameof(UserId), IsUnique = true)] 5 | public class UserPhoto 6 | #if TrackingBase 7 | : TrackingBase 8 | #endif 9 | { 10 | public int UserPhotoId { get; set; } 11 | 12 | public required string UserId { get; set; } 13 | public User? User { get; set; } 14 | 15 | public required byte[] Content { get; set; } 16 | } -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data/Models/Widget.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace Coalesce.Starter.Vue.Data.Models; 4 | 5 | [Description("A sample model provided by the Coalesce template. Remove this when you start building your real data model.")] 6 | public class Widget 7 | #if Tenancy 8 | : TenantedBase 9 | #elif TrackingBase 10 | : TrackingBase 11 | #endif 12 | { 13 | public int WidgetId { get; set; } 14 | 15 | public required string Name { get; set; } 16 | 17 | public required WidgetCategory Category { get; set; } 18 | 19 | public DateTimeOffset? InventedOn { get; set; } 20 | } 21 | 22 | public enum WidgetCategory 23 | { 24 | Whizbangs, 25 | Sprecklesprockets, 26 | Discombobulators, 27 | } 28 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Data/Utilities/QueryableExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Coalesce.Starter.Vue.Data.Utilities; 2 | 3 | public static class QueryableExtensions 4 | { 5 | #if Tenancy 6 | /// 7 | /// Perform an untracked query without any tenant filtering. 8 | /// 9 | public static IQueryable IgnoreTenancy(this IQueryable query) 10 | where T : class 11 | => query.IgnoreQueryFilters().AsNoTrackingWithIdentityResolution(); 12 | #endif 13 | } -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": [ 7 | "streetsidesoftware.code-spell-checker", 8 | "dbaeumer.vscode-eslint", 9 | "vue.volar", 10 | "esbenp.prettier-vscode", 11 | "antfu.goto-alias" 12 | ], 13 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 14 | "unwantedRecommendations": [] 15 | } 16 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules\\typescript\\lib", 3 | "dotnet.defaultSolution": "../Coalesce.Starter.Vue.sln", 4 | "dotnet.automaticallyCreateSolutionInWorkspace": false, 5 | "editor.formatOnSave": true, 6 | "editor.codeActionsOnSave": { 7 | "source.fixAll.eslint": "explicit" 8 | }, 9 | "cSpell.words": ["Vite", "unplugin", "iconsets", "viewmodels", "composables"] 10 | } 11 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/Pages/ConfirmEmail.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Coalesce.Starter.Vue.Web.Pages.ConfirmEmailModel 3 | @{ 4 | ViewData["Title"] = "Confirm email"; 5 | } 6 | 7 |
8 | 9 | @if (ModelState.IsValid) 10 | { 11 |

Thank you for confirming your email.

12 | 13 |
14 | 17 |
18 | } 19 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/Pages/CreateTenant.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Coalesce.Starter.Vue.Web.Pages.CreateTenantModel 3 | 4 | @{ 5 | ViewData["Title"] = "New Organization"; 6 | } 7 | 8 |
9 | 10 |
11 |
12 | 13 | 14 |
15 | 16 |
17 | 20 |
21 |
-------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/Pages/ExternalLogin.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ExternalLoginModel 3 | @{ 4 | ViewData["Title"] = "Sign In"; 5 | } 6 | 7 | 8 | @if (!string.IsNullOrWhiteSpace(Model.ErrorMessage)) 9 | { 10 |

11 | @Model.ErrorMessage 12 |

13 | 14 |
15 | 18 |
19 | } 20 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/Pages/ForgotPassword.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Coalesce.Starter.Vue.Web.Pages.ForgotPasswordModel 3 | @{ 4 | ViewData["Title"] = "Forgot password"; 5 | } 6 | 7 | @if (Model.Success) 8 | { 9 |

10 | If an account matching your input was found, a message with password reset instructions will be sent to the account's email address. 11 |

12 | 13 |
14 | 17 |
18 | } 19 | else 20 | { 21 |
22 |
23 | 24 | 25 |
26 | 27 |
28 | 29 | 30 |
31 | } 32 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/Pages/Invitation.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Coalesce.Starter.Vue.Web.Pages.InvitationModel 3 | @{ 4 | ViewData["Title"] = "Join Organization"; 5 | } 6 | 7 |
8 | 9 | @if (ModelState.IsValid) 10 | { 11 |

12 | You have been invited to join the @Model.Tenant.Name organization. You will join as @User.GetUserName(). 13 |

14 | 15 |
16 | 17 | 20 |
21 | 22 |
23 | 24 | 27 |
28 | 29 |
30 | 33 |
34 | } -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/Pages/SignOut.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @using Microsoft.AspNetCore.Authentication 3 | @model Coalesce.Starter.Vue.Web.Pages.SignOutModel 4 | 5 | @{ 6 | ViewData["Title"] = "Sign Out"; 7 | } 8 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/Pages/SignOut.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Coalesce.Starter.Vue.Data.Models; 2 | using Microsoft.AspNetCore.Identity; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.AspNetCore.Mvc.RazorPages; 5 | 6 | namespace Coalesce.Starter.Vue.Web.Pages; 7 | 8 | public class SignOutModel(SignInManager signInManager) : PageModel 9 | { 10 | public async Task OnGet() 11 | { 12 | await signInManager.SignOutAsync(); 13 | return Redirect("/"); 14 | } 15 | 16 | public async Task OnPost() 17 | { 18 | await signInManager.SignOutAsync(); 19 | return Redirect("/"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Coalesce.Starter.Vue.Data.Auth 2 | @using Coalesce.Starter.Vue.Web.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout.cshtml"; 3 | } -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Kestrel": { 4 | "commandName": "Project", 5 | "launchBrowser": true, 6 | 7 | // Disables injection of MSFT's undesirable JS that force-refreshes the page when C# files are changed. 8 | "hotReloadEnabled": false, 9 | 10 | "environmentVariables": { 11 | "ASPNETCORE_ENVIRONMENT": "Development" 12 | }, 13 | "applicationUrl": "https://localhost:5001" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelliTect/Coalesce/bbbb55fd51a58355ab296730e7b0f40f4f543ca6/templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/public/favicon.ico -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/public/microsoft-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelliTect/Coalesce/bbbb55fd51a58355ab296730e7b0f40f4f543ca6/templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/src/assets/logo.png -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/src/components/HelloWorld.spec.ts: -------------------------------------------------------------------------------- 1 | import { mount } from "@/test-utils"; 2 | import HelloWorld from "./HelloWorld.vue"; 3 | 4 | describe("HelloWorld.vue", () => { 5 | it("renders props.msg when passed", () => { 6 | const msg = "new message"; 7 | const wrapper = mount(HelloWorld, { 8 | props: { msg }, 9 | }); 10 | expect(wrapper.text()).toMatch(msg); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/src/composables/useForm.ts: -------------------------------------------------------------------------------- 1 | import { VForm } from "vuetify/components"; 2 | 3 | /** Returns a ref suitable for holding an instance of a Vuetify VForm. 4 | * 5 | * @param eagerValidate If true (default), the form will be eagerly validated, 6 | * showing error messages immediately instead of waiting for submission 7 | * or an explicit validate() call. 8 | */ 9 | export function useForm(eagerValidate = true) { 10 | const form = ref(); 11 | 12 | // Eagerly validate as new controls are added or removed. 13 | if (eagerValidate) { 14 | watch( 15 | () => JSON.stringify(form.value?.items.map((i) => i.id)), 16 | () => { 17 | form.value?.validate(); 18 | }, 19 | ); 20 | } 21 | 22 | return form; 23 | } 24 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/src/composables/useUser.ts: -------------------------------------------------------------------------------- 1 | import { 2 | userInfo, 3 | //#if Identity 4 | can, 5 | //#endif 6 | } from "@/user-service"; 7 | 8 | export function useUser() { 9 | return { 10 | userInfo, 11 | //#if Identity 12 | can, 13 | //#endif 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/src/styles/site.scss: -------------------------------------------------------------------------------- 1 | a { 2 | text-decoration: none; 3 | } 4 | 5 | // Vuetify3 lost the padding for ul/ol that Vuetify2 had. Restore it. 6 | .v-application { 7 | ul, 8 | ol { 9 | padding-left: 24px; 10 | } 11 | } 12 | 13 | // Details under inputs are bottom-aligned by default. Undo that. 14 | .v-input__details { 15 | align-items: normal; 16 | padding-top: 3px; 17 | overflow: visible; 18 | } 19 | 20 | .v-icon { 21 | // Vuetify v-icon expects to be inline-flex, 22 | // but fontawesome defaults to inline-block through this var. 23 | // Without setting this, icon appearance can vary with CSS load order 24 | --fa-display: inline-flex; 25 | } 26 | 27 | // Visually distinguish readonly inputs 28 | .v-input--readonly { 29 | .v-switch__track, 30 | .v-switch__thumb { 31 | border: 1px dashed rgba(var(--v-theme-on-surface), 0.7); 32 | } 33 | .v-field__outline__start, 34 | .v-field__outline__end, 35 | .v-field__outline__notch::before, 36 | .v-field__outline__notch::after { 37 | border-style: dashed; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/src/types/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | declare global { 5 | declare const BUILD_DATE: Date | null | undefined; 6 | declare const ASPNETCORE_ENVIRONMENT: string; 7 | } 8 | 9 | export {}; 10 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/src/views/OpenAPI.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 23 | 24 | 29 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/src/views/WidgetEdit.spec.ts: -------------------------------------------------------------------------------- 1 | import { mount, flushPromises, mockEndpoint } from "@/test-utils"; 2 | 3 | import { VTextField } from "vuetify/components"; 4 | import WidgetEdit from "./WidgetEdit.vue"; 5 | import { Widget, WidgetCategory } from "@/models.g"; 6 | 7 | describe("WidgetEdit.vue", () => { 8 | it("loads user id 1", async () => { 9 | // Arrange 10 | mockEndpoint("/Widget/get", () => ({ 11 | wasSuccessful: true, 12 | object: { 13 | widgetId: 1, 14 | name: "Whirlygig", 15 | category: WidgetCategory.Sprecklesprockets, 16 | } as Widget, 17 | })); 18 | 19 | // Act 20 | const wrapper = mount(WidgetEdit, { props: { id: 1 } }); 21 | await flushPromises(); 22 | 23 | // Assert 24 | expect(wrapper.text()).toMatch("Editing Widget: 1"); 25 | expect(document.title).toMatch("Whirlygig"); 26 | expect(wrapper.findComponent(VTextField).vm.modelValue).toBe("Whirlygig"); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/src/views/errors/NotFound.vue: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "moduleResolution": "node", 8 | "incremental": true, 9 | "tsBuildInfoFile": "./node_modules/.tsbuildinfo", 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "allowSyntheticDefaultImports": true, 13 | "noEmit": true, 14 | "baseUrl": ".", 15 | "paths": { 16 | "@/*": ["src/*"] 17 | } 18 | }, 19 | "include": [ 20 | "src/**/*.ts", 21 | "src/**/*.tsx", 22 | "src/**/*.vue", 23 | "tests/**/*.ts", 24 | "tests/**/*.tsx" 25 | ], 26 | "references": [{ "path": "./tsconfig.node.json" }] 27 | } 28 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "types": ["node", "vitest"] 7 | }, 8 | "include": ["vite.config.ts"], 9 | "exclude": ["node_modules"] 10 | } 11 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Coalesce.Starter.Vue.Web/web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12.0 4 | true 5 | enable 6 | 7 | true 8 | 9 | 5.3.2 10 | 11 | 12 | $(DefineConstants);KeepTemplateOnly;AppInsights;OpenAPI;AuditLogs;TrackingBase;ExampleModel 13 | $(DefineConstants);Identity;UserPictures;MicrosoftAuth;GoogleAuth;LocalAuth 14 | $(DefineConstants);EmailAzure;EmailSendGrid 15 | $(DefineConstants);Tenancy;TenantPerHostname;TenantCreateSelf;TenantCreateExternal;TenantCreateAdmin;TenantMemberInvites 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /templates/Coalesce.Vue.Template/content/coalesce.json: -------------------------------------------------------------------------------- 1 | { 2 | "webProject": { 3 | "projectFile": "./Coalesce.Starter.Vue.Web/Coalesce.Starter.Vue.Web.csproj" 4 | }, 5 | "dataProject": { 6 | "projectFile": "./Coalesce.Starter.Vue.Data/Coalesce.Starter.Vue.Data.csproj" 7 | }, 8 | 9 | "rootGenerator": "Vue", 10 | 11 | "generatorConfig": { 12 | "Scripts": { 13 | "targetDirectory": "../src" 14 | } 15 | } 16 | } --------------------------------------------------------------------------------