pdevito3 / craftsman Goto Github PK
View Code? Open in Web Editor NEWA .NET scaffolding tool to help you stop worrying about boilerplate and focus on your business logic ๐
Home Page: https://wrapt.dev
License: MIT License
A .NET scaffolding tool to help you stop worrying about boilerplate and focus on your business logic ๐
Home Page: https://wrapt.dev
License: MIT License
Currently, gateways do not have authorization scaffolding built in and can not be used with apis that have authorization protection.
One should be able to create a new gateway that complements an authorization protected api and pass authorization info through that gateway.
I'd expect to be able to submit a POST and get 201, but am getting 500
A build like the below will have a broken POSt because PK is expecting ReportRequestId
instad of ReportRequest
. Need to fix to use primary key
DomainName: BrokenPost
BoundedContexts:
- SolutionName: Reporting
Port: 1210
DbContext:
ContextName: ReportingDbContext
DatabaseName: ReportingDbContext
Provider: SqlServer
Entities:
- Name: ReportRequest
Properties:
- Name: ReportId
IsPrimaryKey: true
Type: guid
CanFilter: true
CanSort: true
- Name: Provider
Type: string
CanFilter: true
CanSort: true
- Name: Target
Type: string
CanFilter: true
CanSort: true
SwaggerConfig:
Title: RideRequest Swgger Doc
Description: This is my swagger doc
SwaggerEndpointName: "v1"
AddSwaggerComments: true
Craftsman version (dotnet tool list -g
): 0.9.3
The integration tests fail to run on macOS with M1 because Craftsman relies on mssql server image which has not yet been port to ARM64. The current workaround is to use azure sql edge. See this blog for details.
Craftsman version (dotnet tool list -g):
โฏ dotnet tool list -g
Package Id Version Commands
--------------------------------------
craftsman 0.12.3 craftsman
dotnet-ef 6.0.1 dotnet-ef
Environment info
โฏ dotnet --info
.NET SDK (reflecting any global.json):
Version: 6.0.101
Commit: ef49f6213a
Runtime Environment:
OS Name: Mac OS X
OS Version: 12.1
OS Platform: Darwin
RID: osx.12-arm64
Base Path: /usr/local/share/dotnet/sdk/6.0.101/
Host (useful for support):
Version: 6.0.1
Commit: 3a25a7f1cc
.NET SDKs installed:
6.0.101 [/usr/local/share/dotnet/sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 6.0.1 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 6.0.1 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
It took a few hours to figure this error. So we could do few things to help anyone else running into this issue:
Option 1: It might be worth to call this out in the getting started docs for folks with M1 chip
Option 2: Detect at Craftsman builder runtime (aka new:*
commands) that the machine is a macOS with arm64 and generate azure-sql-edge
Option 3: At test runtime detect that the machine is a macOS with arm64 and use azure-sql-edge image. Can be found using the following:
> System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture
Arm64
> System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
true
Option 3 seems like a better solution since it would be clear in the code as why azure-sql-edge was chosen over ms-sql-server with some comments. I'd be happy to make a PR if your prefer option 3!
If entity has the DeleteRecord feature and the singular and plural names are the same it breaks the associated integration test. Variables in the test are both the same name
Craftsman version (dotnet tool list -g
): 11.1
Hi,
i really like wrapt.dev and would like to test it with this simple setup:
On my workstation mystation i use a sqlexpress instance like a normal sqlserver.I did also create a database test.
I did test craftman with this yaml file:
SolutionName: Test.Api
DbContext:
ContextName: TestDbContext
DatabaseName: test
Provider: SqlServer
Entities:
- Name: Info
Properties:
- Name: InfoId
IsPrimaryKey: true
Type: int
CanFilter: true
CanSort: true
- Name: MyInfo
Type: string
CanFilter: true
CanSort: true
- Name: MySpeed
Type: int
CanFilter: true
CanSort: true
- Name: MyType
Type: string
CanFilter: true
CanSort: true
Environments:
- EnvironmentName: Startup
ConnectionString: "Data Source=mystation\\sqlexpress;Database=test;Integrated Security=false;User ID=sa;Password=start;"
ProfileName: Prod
SwaggerConfig:
Title: Test WebApi
Description: Our API uses a REST based design, leverages the JSON data format, and relies upon HTTPS for transport. We respond with meaningful HTTP response codes and if an error occurs, we include error details in the response body. API Documentation is at xxxxx/dev/docs
AddSwaggerComments: true
I do only get a message "Your template file was parsed successfully." but it stucks here and does not complete..
Is there way to enable verbose debugging, so i can see where the problem resists ?
An entity without defined features creates a empty controller, it contains non existent namespace reference (maybe if no features, don't create controller)
Craftsman version (dotnet tool list -g
): 11.1
I tried the create new example per docs on macOS arm64 and it throws the following error.
โฏ craftsman new:example
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Create an Example Project โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
What would you like to name this project (e.g. MyExampleProject)? Craftsman01
File scaffolding for RecipeManagement was successful.
Database Migrations for RecipeManagement were successful.
TypeInitializationException: The type initializer for 'LibGit2Sharp.Core.NativeMethods' threw an
exception.
In order to help diagnose loading problems, consider setting the DYLD_PRINT_LIBRARIES environment
variable: dlopen(libgit2-106a5f2, 0x0001): tried: 'libgit2-106a5f2' (no such file),
'/usr/local/lib/libgit2-106a5f2' (no such file), '/usr/lib/libgit2-106a5f2' (no such file),
'/Users/me/Desktop/code/csharp/libgit2-106a5f2' (no such file),
'/usr/local/lib/libgit2-106a5f2' (no such file), '/usr/lib/libgit2-106a5f2' (no such file)
at git_libgit2_init()
at InitializeNativeLibrary()
at cctor()
at git_repository_init_ext(git_repository*& repository, FilePath path, GitRepositoryInitOptions options)
at git_repository_init_ext(FilePath workdirPath, FilePath gitdirPath, Boolean isBare)
at Init(String path, Boolean isBare)
at Init(String path)
at GitSetup(String solutionDirectory) in Utilities.cs:393
at CreateNewDomainProject(String domainDirectory, IFileSystem fileSystem, DomainProject domainProject)
in NewDomainProjectCommand.cs:115
at Run(String buildSolutionDirectory, IFileSystem fileSystem) in NewExampleCommand.cs:41
Craftsman version (dotnet tool list -g
):
โฏ dotnet tool list -g
Package Id Version Commands
--------------------------------------
craftsman 0.12.3 craftsman
dotnet-ef 6.0.1 dotnet-ef
Environment info
โฏ dotnet --info
.NET SDK (reflecting any global.json):
Version: 6.0.101
Commit: ef49f6213a
Runtime Environment:
OS Name: Mac OS X
OS Version: 12.1
OS Platform: Darwin
RID: osx.12-arm64
Base Path: /usr/local/share/dotnet/sdk/6.0.101/
Host (useful for support):
Version: 6.0.1
Commit: 3a25a7f1cc
.NET SDKs installed:
6.0.101 [/usr/local/share/dotnet/sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 6.0.1 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 6.0.1 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
To install additional .NET runtimes or SDKs:
https://aka.ms/dotnet-download
Git version
โฏ git --version
git version 2.34.1
Libgit2 info
โฏ brew info libgit2
libgit2: stable 1.3.0 (bottled), HEAD
C library of Git core methods that is re-entrant and linkable
https://libgit2.github.com/
/opt/homebrew/Cellar/libgit2/1.3.0 (102 files, 3.5MB) *
Poured from bottle on 2021-12-21 at 11:05:13
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/libgit2.rb
License: GPL-2.0-only
==> Dependencies
Build: cmake โ, pkg-config โ
Required: libssh2 โ
==> Options
--HEAD
Install HEAD version
==> Analytics
install: 5,007 (30 days), 23,757 (90 days), 60,775 (365 days)
install-on-request: 1,839 (30 days), 12,159 (90 days), 31,213 (365 days)
build-error: 0 (30 days)
I tried this with few different example type (BasicAuth, FK) and they all throw the same error.
The path separator character needs to be OS agnostic instead of Windows specific.
Had a report on discord for the below that I need to investigate.
Thank you always for this great work, the BaseEntity properties have private setters, but you are trying to assign them in UpdateAuditFields method of the DbContext class directly which raises an exception.
When the UserPolicyHandler generates it uses RecipesDbContext instead of the defined context name from the definition file.
The script stops after displaying the message
Your template file was parsed successfully.
The script creates a solution in a sub-folder, but it doesn't have any entities or swagger in it.
Craftsman version (dotnet tool list -g
): 0.8.1
###Yaml file used
ApiTemplateName.zip
First I must say that Wrapt is a FANTASTIC work. Super useful. Congratulations.
But I noticed that all "Id" fields generated by it are Guid type, which translates to a UNIQUEIDENTIFIER datatype in SQL Server.
This datatype takes 16 bytes per row, which grows really fast on tables with lots of records.
It would be great if we can specify the data type of the Id field... Default can still be Guid, but it would be nice to have the option to choose between Byte (TinyInt), Int16 (SmallInt), Int32 (Int) or Int64 (BigInt). Some tables has a very predictable number of rows and it would be nice to save some bytes by allocating only the necessary amount of space to that field.
Greetings from Brazil ๐ง๐ท
Some integration tests will fail when using an int primary key.
Use a template with an int primary key
Craftsman version (dotnet tool list -g
): 0.9.2
I created a YAML file and generated the projects. API is running properly, but when I try to run the migrations command it is failing. Screenshot of the Powershell migration command output.
dotnet ef migrations add "InitialMigration" --project Infrastructure.Persistence --startup-project WebApi --output-dir Migrations
SolutionName: TodoApi
DbContext:
ContextName: TodoDbContext
DatabaseName: TodoDb
Provider: SqlServer
Entities:
- Name: TodoItem
Properties:
- Name: TodoId
IsPrimaryKey: true
Type: int
CanFilter: true
CanSort: true
- Name: Name
Type: string
CanFilter: true
CanSort: true
- Name: IsCompleted
Type: bool
CanFilter: true
CanSort: true
Environments:
- EnvironmentName: Startup
ConnectionString: "Data Source=.;Initial Catalog=TodoDb;Integrated Security=True;Encrypt=True;TrustServerCertificate=True;"
ProfileName: Dev
SwaggerConfig:
Title: Todo Api Service
Description: API for managing todo items.
ApiContact:
Name: Anuraj
Email: [email protected]
Url: https://dotnetthoughts.net
Build started...
Build succeeded.
System.InvalidOperationException: Unable to resolve service for type 'Microsoft.EntityFrameworkCore.Migrations.IMigrator'. This is often because no database provider has been configured for this DbContext. A provider can be configured by overriding the 'DbContext.OnConfiguring' method or by using 'AddDbContext' on the application service provider. If 'AddDbContext' is used, then also ensure that your DbContext type accepts a DbContextOptions<TContext> object in its constructor and passes it to the base constructor for DbContext.
at Microsoft.EntityFrameworkCore.Infrastructure.Internal.InfrastructureExtensions.GetService[TService](IInfrastructure`1 accessor)
at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
at Microsoft.EntityFrameworkCore.Design.DesignTimeServiceCollectionExtensions.<>c__DisplayClass1_0.<AddDbContextDesignTimeServices>b__7(IServiceProvider _)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.EnsureServices(IServiceProvider services)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType, String namespace)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType, String namespace)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_0.<.ctor>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
Unable to resolve service for type 'Microsoft.EntityFrameworkCore.Migrations.IMigrator'. This is often because no database provider has been configured for this DbContext. A provider can be configured by overriding the 'DbContext.OnConfiguring' method or by using 'AddDbContext' on the application service provider. If 'AddDbContext' is used, then also ensure that your DbContext type accepts a DbContextOptions<TContext> object in its constructor and passes it to the base constructor for DbContext.
Craftsman version (dotnet tool list -g
): 0.7.0
Wanted to try this out and followed the tutorial. Wrote this yaml file:
SolutionName: SoftThornApi
DbContext:
ContextName: SoftThornDbContext
DatabaseName: SoftThorn
Provider: SqlServer
Entities:
- Name: Product
Properties:
- Name: ProductId
IsPrimaryKey: true
Type: int
CanFilter: true
CanSort: true
- Name: Name
Type: string
CanFilter: true
CanSort: true
- Name: ProductImageId
IsPrimaryKey: false
Type: int?
CanFilter: true
CanSort: false
- Name: ProductImage
Properties:
- Name: ProductImageId
IsPrimaryKey: true
Type: int
CanFilter: true
CanSort: true
- Name: Image
Type: byte[]
CanFilter: false
CanSort: false
Environments:
- EnvironmentName: Production
ConnectionString: "Data Source=myprodserver;Initial Catalog=CarbonKitchen;Integrated Security=True;Encrypt=True;TrustServerCertificate=True;"
ProfileName: Prod
- EnvironmentName: Qa
ConnectionString: "Data Source=myqaserver;Initial Catalog=CarbonKitchen;Integrated Security=True;Encrypt=True;TrustServerCertificate=True;"
ProfileName: Qa
SwaggerConfig:
Title: SoftThorn
Description: Our API uses a REST based design, leverages the JSON data format, and relies upon HTTPS for transport. We respond with meaningful HTTP response codes and if an error occurs, we include error details in the response body. API Documentation is at carbonkitchen.com/dev/docs
ApiContact:
Name: SoftThorn
Email: [email protected]
Url: localhost
craftsman new:api D:\Drop\Desktop\SoftThorn.yaml
cli output:
An unhandled exception occurred when running the API command.
The error details are:
The `E:\Code\SoftThorn\SoftThornApi\Domain\Entities` directory could not be found.
When creating that folder manually, it complains that the folder already exists.
Craftsman version (dotnet tool list -g
): craftsman 0.4.2 craftsman
Foundation version (dotnet new -u
): wasnt isntalled for some reason
Hello,
When I created a new:domain the resulting project contained Entities that contained an ID field of type GUID that was autogenerated by craftsman. I am wondering if there is a way to define the data type for the ID? Right now if I add an Id as a Property to my Entity then two Id fields are created resulting in build errors. The use case I have is that the Id field needs to be an INT because the legacy database we are using uses autoincremented Ids.
A https://github.com/gothinkster/realworld backend made with craftsman would be valuable because then you can show how far the generation will take you.
When using a MSSQL server for the database back-end and hitting a base route for an entity, entity framework gives the following warning:
warn: Microsoft.EntityFrameworkCore.Query[10102]
The query uses a row limiting operator ('Skip'/'Take') without an 'OrderBy' operator. This may lead to unpredictable results.
Here is an example reproduction:
Set up a local MSSQL server and create a database with the name ExampleDb.
Adjust the MSSQL username and password below, and save the following Example.API.yaml
file.
SolutionName: Example.API
AddGit: false
DbContext:
ContextName: ExampleDbContext
DatabaseName: ExampleDb
Provider: SqlServer
Entities:
- Name: MyEntity
Properties:
- Name: MyEntityId
IsPrimaryKey: true
Type: int
CanFilter: true
CanSort: true
- Name: Title
Type: string
CanFilter: true
CanSort: true
Environments:
- EnvironmentName: Local
ConnectionString: "Data Source=localhost;Initial Catalog=ExampleDb;User Id=sa;Password=P@$$w0rd;"
ProfileName: Local
echo 'n' | craftsman new:api ${PROJECT_NAME}.yaml
echo ""
cd Example.API
export ASPNETCORE_ENVIRONMENT=Local
dotnet ef migrations add "InitialMigration" --project Infrastructure.Persistence --startup-project WebApi --output-dir Migrations
dotnet ef database update --project Infrastructure.Persistence --startup-project WebApi
cd WebApi
dotnet run --launch-profile Local
/api/MyEntitys
route and note the warnings in the shell window.I should note that I'm working on a Mac running macOS 11.1 (Big Sur) and dotnet 5.
If you're open to it, here is a possible solution:
master...nm777:default-sort-order
Currently, when an entity is created with a Guid PK, it is not added to the creation DTO. This means that there is no way to add an entity with a DTO and it will always get added with 000-000...
Two things should be added:
I've encounterd something very strange..
When i'm trying to execute a GET request to retrieve a list of items, i can also specify a filter but when those values are wrong ( ex. guid == leaves) the result still gives me a complete list with all the entities back from the database. ( ex. GetUsers returns all the users)
It looks like there is no validation on the filters.
How do i return an error when the result of the filter returns nothing?
Hi there!
We have created a service using craftsman that we intend to run in AWS EKS. To handle service to service communication and AWS resource orchestration we are looking at using DAPR. Have you looked at integrating DAPR into wrapt and craftsman before? Do you have any thoughts on using DAPR?
I noticed that when the code is scaffolded that the Controller uses Controller not ControllerBase. Based on the docs that Microsoft provides, I think this should use ControllerBase
Hi Paul,
Great work on the craftsman project. I'm looking forward to being a hardcore user of wrapt. Spent about a day on the documentation and also looking at the source code. I see a lot of changes that were made in 0.11.x aren't updated on the website.
Would appreciate it if you could update the documentation.
Best,
Deepak
Hello, this is fantastic, can you please allow us to scaffold basic views/tables/grid just like the default ASP MVC CRUD views
https://github.com/ZeekoZhu/TextTemplatingCore
https://github.com/muhammadazam/MVC-T4-Templates/blob/master/CodeTemplates/MvcControllerWithContext/Controller.cs.t4
https://github.com/ligershark/side-waffle/tree/master/TemplatePack/ItemTemplates/Web/ASP.NET/Custom%20T4%20Files/MvcView
ModelMetadataFunctions.cs.include.t4
<#+
// Gets the related entity information for an association property where there is an associated foreign key property.
// Note: ModelMetadata.RelatedEntities contains only the related entities associated through a foreign key property.
RelatedModelMetadata GetRelatedModelMetadata(PropertyMetadata property){
RelatedModelMetadata propertyModel;
IDictionary<string, RelatedModelMetadata> relatedProperties;
if(ModelMetadata.RelatedEntities != null)
{
relatedProperties = ModelMetadata.RelatedEntities.ToDictionary(item => item.AssociationPropertyName);
}
else
{
relatedProperties = new Dictionary<string, RelatedModelMetadata>();
}
relatedProperties.TryGetValue(property.PropertyName, out propertyModel);
return propertyModel;
}
// A foreign key, e.g. CategoryID, will have an association name of Category
string GetAssociationName(PropertyMetadata property) {
RelatedModelMetadata propertyModel = GetRelatedModelMetadata(property);
return propertyModel != null ? propertyModel.AssociationPropertyName : property.PropertyName;
}
// A foreign key, e.g. CategoryID, will have a value expression of Category.CategoryID
string GetValueExpression(PropertyMetadata property) {
RelatedModelMetadata propertyModel = GetRelatedModelMetadata(property);
return propertyModel != null ? propertyModel.AssociationPropertyName + "." + propertyModel.DisplayPropertyName : property.PropertyName;
}
// This will return the primary key property name, if and only if there is exactly
// one primary key. Returns null if there is no PK, or the PK is composite.
string GetPrimaryKeyName() {
return (ModelMetadata.PrimaryKeys != null && ModelMetadata.PrimaryKeys.Count() == 1) ? ModelMetadata.PrimaryKeys[0].PropertyName : null;
}
bool IsPropertyGuid(PropertyMetadata property) {
return String.Equals("System.Guid", property.TypeName, StringComparison.OrdinalIgnoreCase);
}
#>
Imports.include.t4
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Data.Entity" #>
<#@ assembly name="System.Data.Linq" #>
<#@ ScaffoldingAssembly Processor="ScaffoldingAssemblyLoader" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Data.Linq.Mapping" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="Microsoft.AspNet.Scaffolding.Core.Metadata" #>
<#@ parameter type="System.String" name="ViewDataTypeName" #>
<#@ parameter type="System.String" name="ViewDataTypeShortName" #>
<#@ parameter type="System.Boolean" name="IsPartialView" #>
<#@ parameter type="System.Boolean" name="IsLayoutPageSelected" #>
<#@ parameter type="System.Boolean" name="ReferenceScriptLibraries" #>
<#@ parameter type="System.Boolean" name="IsBundleConfigPresent" #>
<#@ parameter type="System.String" name="ViewName" #>
<#@ parameter type="System.String" name="LayoutPageFile" #>
<#@ parameter type="System.String" name="JQueryVersion" #>
<#@ parameter type="Microsoft.AspNet.Scaffolding.Core.Metadata.ModelMetadata" name="ModelMetadata" #>
<#@ parameter type="System.Version" name="MvcVersion" #>
View With controller context
<#@ template language="C#" HostSpecific="True" Debug="True" #>
<#@ output extension="cs" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Data.Linq" #>
<#@ ScaffoldingAssembly Processor="ScaffoldingAssemblyLoader" #>
<#
string routePrefix;
if (String.IsNullOrEmpty(AreaName))
{
routePrefix = ControllerRootName;
}
else
{
routePrefix = AreaName + "/" + ControllerRootName;
}
#>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="Microsoft.AspNet.Scaffolding.Core.Metadata" #>
<#@ parameter type="System.String" name="ControllerName" #>
<#@ parameter type="System.String" name="ControllerRootName" #>
<#@ parameter type="System.String" name="Namespace" #>
<#@ parameter type="System.String" name="AreaName" #>
<#@ parameter type="System.String" name="ContextTypeName" #>
<#@ parameter type="System.String" name="ModelTypeName" #>
<#@ parameter type="System.String" name="ModelVariable" #>
<#@ parameter type="Microsoft.AspNet.Scaffolding.Core.Metadata.ModelMetadata" name="ModelMetadata" #>
<#@ parameter type="System.String" name="EntitySetVariable" #>
<#@ parameter type="System.Boolean" name="UseAsync" #>
<#@ parameter type="System.Boolean" name="IsOverpostingProtectionRequired" #>
<#@ parameter type="System.String" name="BindAttributeIncludeText" #>
<#@ parameter type="System.String" name ="OverpostingWarningMessage" #>
<#@ parameter type="System.Collections.Generic.HashSet<System.String>" name="RequiredNamespaces" #>
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
<# if (UseAsync) { #>
using System.Threading.Tasks;
<# } #>
using System.Net;
using System.Web;
using System.Web.Mvc;
<# foreach (var namespaceName in RequiredNamespaces) { #>
using <#= namespaceName #>;
<# } #>
namespace <#= Namespace #>
{
<#
var contextTypeName = ContextTypeName;
var entitySetName = ModelMetadata.EntitySetName;
var entitySetVar = EntitySetVariable ?? (String.IsNullOrEmpty(entitySetName) ? entitySetName : (entitySetName.Substring(0, length:1).ToLowerInvariant() + entitySetName.Substring(1)));
var primaryKeyName = ModelMetadata.PrimaryKeys[0].PropertyName;
var primaryKeyShortTypeName = ModelMetadata.PrimaryKeys[0].ShortTypeName;
var primaryKeyDefaultValue = ModelMetadata.PrimaryKeys[0].DefaultValue;
var primaryKeyType = ModelMetadata.PrimaryKeys[0].TypeName;
var primaryKeyNullableTypeName = GetNullableTypeName(primaryKeyType, primaryKeyShortTypeName);
var lambdaVar = ModelVariable[0];
var relatedProperties = ModelMetadata.RelatedEntities.ToDictionary(item => item.AssociationPropertyName);
string bindAttribute;
if (IsOverpostingProtectionRequired)
{
bindAttribute = String.Format("[Bind(Include = \"{0}\")] ", BindAttributeIncludeText);
}
else
{
bindAttribute = String.Empty;
}
#>
public class <#= ControllerName #> : Controller
{
private <#= ContextTypeName #> db = new <#= ContextTypeName #>();
// GET: <#= routePrefix #>
<# if (UseAsync) { #>
public async Task<ActionResult> Index()
<# } else { #>
public ActionResult Index(int page = 1, int pageSize = 10, string sortBy = "Id", bool isAsc = true, string search = null)
<# } #>
{
<# var includeExpressions = "";
includeExpressions = String.Join("", relatedProperties.Values.Select(property => String.Format(".Include({0} => {0}.{1})", lambdaVar, property.AssociationPropertyName)));
#>
<# if(!String.IsNullOrEmpty(includeExpressions)) { #>
var <#= entitySetVar #> = db.<#= entitySetName #><#= includeExpressions #>;
// Search Logic
<#= entitySetVar #> = <#= entitySetVar #>.Where(s => search == null<#
int i=0;
var properties = ModelMetadata.Properties;
foreach (PropertyMetadata property in properties) {
if (property.Scaffold && !property.IsPrimaryKey && !property.IsForeignKey && !property.IsComplexType) {
if(IsString(property.TypeName)){#>
|| (s.<#= GetValueExpression(property) #> != null && s.<#= GetValueExpression(property) #>.ToLower().Contains(search.ToLower()))<#
}
else if(IsBool(property.TypeName)){#>
|| (s.<#= GetValueExpression(property) #>.ToString().ToLower().Contains(search.ToLower()))<#
}
else if(HasValueExpression(property)){#>
//// Todo: This makes the Query slow. Need to find a better way
//|| (s.<#= GetValueExpression(property) #> != null && s.<#= GetValueExpression(property) #>.ToString().ToLower().Contains(search.ToLower()))<#
}
else{#>
<#}
}
}#>
);
// End Search Logic
ViewBag.TotalPages = (int)Math.Ceiling((double)<#= entitySetVar #>.Count() / pageSize);
ViewBag.CurrentPage = page;
ViewBag.PageSize = pageSize;
ViewBag.Search = search;
ViewBag.SortBy = sortBy;
ViewBag.IsAsc = isAsc;
<# if (UseAsync) { #>
return View(await <#= entitySetVar #>.OrderBy(o=>o.<#= primaryKeyName #>).Skip((page - 1) * pageSize).Take(pageSize).ToListAsync());
<# } else { #>
return View(<#= entitySetVar #>.OrderBy(o=>o.<#= primaryKeyName #>).Skip((page - 1) * pageSize).Take(pageSize).ToList());
<# } #>
<# } else { #>
// Search Logic
var <#= entitySetVar #> = db.<#= entitySetName #>.Where(s => search == null<#
int i=0;
var properties = ModelMetadata.Properties;
foreach (PropertyMetadata property in properties) {
if (property.Scaffold && !property.IsPrimaryKey && !property.IsForeignKey && !property.IsComplexType) {
if(IsString(property.TypeName)){#>
|| (s.<#= GetValueExpression(property) #> != null && s.<#= GetValueExpression(property) #>.ToLower().Contains(search.ToLower()))<#
}
else if(IsBool(property.TypeName)){#>
|| (s.<#= GetValueExpression(property) #>.ToString().ToLower().Contains(search.ToLower()))<#
}
else if(HasValueExpression(property)){#>
//// Todo: This makes the Query slow. Need to find a better way
//|| (s.<#= GetValueExpression(property) #> != null && s.<#= GetValueExpression(property) #>.ToString().ToLower().Contains(search.ToLower()))<#
}
else{#>
<#}
}
}#>
);
// End Search Logic
ViewBag.TotalPages = (int)Math.Ceiling((double)<#= entitySetVar #>.Count() / pageSize);
ViewBag.CurrentPage = page;
ViewBag.PageSize = pageSize;
ViewBag.Search = search;
ViewBag.SortBy = sortBy;
ViewBag.IsAsc = isAsc;
<# if (UseAsync) { #>
return View(await <#= entitySetVar #><#= includeExpressions #>.OrderBy(o=>o.<#= primaryKeyName #>).Skip((page - 1) * pageSize).Take(pageSize).ToListAsync());
<# } else { #>
return View(<#= entitySetVar #><#= includeExpressions #>.OrderBy(o=>o.<#= primaryKeyName #>).Skip((page - 1) * pageSize).Take(pageSize).ToList());
<# } #>
<# } #>
}
// GET: <#= routePrefix #>/Details/5
<# if (UseAsync) { #>
public async Task<ActionResult> Details(<#= primaryKeyNullableTypeName #> id)
<# } else { #>
public ActionResult Details(<#= primaryKeyNullableTypeName #> id)
<# } #>
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
<# if (UseAsync) { #>
<#= ModelTypeName #> <#= ModelVariable #> = await db.<#= entitySetName #>.FindAsync(id);
<# } else { #>
<#= ModelTypeName #> <#= ModelVariable #> = db.<#= entitySetName #>.Find(id);
<# } #>
if (<#= ModelVariable #> == null)
{
return HttpNotFound();
}
return View(<#= ModelVariable #>);
}
// GET: <#= routePrefix #>/Create
public ActionResult Create()
{
<# foreach (var property in relatedProperties.Values) { #>
ViewBag.<#= property.ForeignKeyPropertyNames[0] #> = new SelectList(db.<#= property.EntitySetName #>, "<#= property.PrimaryKeyNames[0] #>", "<#= property.DisplayPropertyName #>");
<# } #>
return View();
}
// POST: <#= routePrefix #>/Create
<# if (IsOverpostingProtectionRequired) {
foreach (var line in OverpostingWarningMessage.Split(new string[] { Environment.NewLine }, StringSplitOptions.None)) {
#>
// <#= line #>
<# } } #>
[HttpPost]
[ValidateAntiForgeryToken]
<# if (UseAsync) { #>
public async Task<ActionResult> Create(<#= bindAttribute #><#= ModelTypeName #> <#= ModelVariable #>)
<# } else { #>
public ActionResult Create(<#= bindAttribute #><#= ModelTypeName #> <#= ModelVariable #>)
<# } #>
{
if (ModelState.IsValid)
{
<# if(!String.IsNullOrEmpty(primaryKeyType) && String.Equals("System.Guid", primaryKeyType, StringComparison.OrdinalIgnoreCase)) { #>
<#= ModelVariable #>.<#= primaryKeyName #> = Guid.NewGuid();
<# } #>
db.<#= entitySetName #>.Add(<#= ModelVariable #>);
<# if (UseAsync) {#>
await db.SaveChangesAsync();
<# } else { #>
db.SaveChanges();
<# } #>
return RedirectToAction("Index");
}
<# foreach (var property in relatedProperties.Values) { #>
ViewBag.<#= property.ForeignKeyPropertyNames[0] #> = new SelectList(db.<#= property.EntitySetName #>, "<#= property.PrimaryKeyNames[0] #>", "<#= property.DisplayPropertyName #>", <#= ModelVariable #>.<#= property.ForeignKeyPropertyNames[0] #>);
<# } #>
return View(<#= ModelVariable #>);
}
// GET: <#= routePrefix #>/Edit/5
<# if (UseAsync) { #>
public async Task<ActionResult> Edit(<#= primaryKeyNullableTypeName #> id)
<# } else { #>
public ActionResult Edit(<#= primaryKeyNullableTypeName #> id)
<# } #>
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
<# if (UseAsync) { #>
<#= ModelTypeName #> <#= ModelVariable #> = await db.<#= entitySetName #>.FindAsync(id);
<# } else { #>
<#= ModelTypeName #> <#= ModelVariable #> = db.<#= entitySetName #>.Find(id);
<# } #>
if (<#= ModelVariable #> == null)
{
return HttpNotFound();
}
<# foreach (var property in relatedProperties.Values) { #>
ViewBag.<#= property.ForeignKeyPropertyNames[0] #> = new SelectList(db.<#= property.EntitySetName #>, "<#= property.PrimaryKeyNames[0] #>", "<#= property.DisplayPropertyName #>", <#= ModelVariable #>.<#= property.ForeignKeyPropertyNames[0] #>);
<# } #>
return View(<#= ModelVariable #>);
}
// POST: <#= routePrefix #>/Edit/5
<# if (IsOverpostingProtectionRequired) {
foreach (var line in OverpostingWarningMessage.Split(new string[] { Environment.NewLine }, StringSplitOptions.None)) {
#>
// <#= line #>
<# } } #>
[HttpPost]
[ValidateAntiForgeryToken]
<# if (UseAsync) { #>
public async Task<ActionResult> Edit(<#= bindAttribute #><#= ModelTypeName #> <#= ModelVariable #>)
<# } else { #>
public ActionResult Edit(<#= bindAttribute #><#= ModelTypeName #> <#= ModelVariable #>)
<# } #>
{
if (ModelState.IsValid)
{
db.Entry(<#= ModelVariable #>).State = EntityState.Modified;
<# if (UseAsync) { #>
await db.SaveChangesAsync();
<# } else { #>
db.SaveChanges();
<# } #>
return RedirectToAction("Index");
}
<# foreach (var property in relatedProperties.Values) { #>
ViewBag.<#= property.ForeignKeyPropertyNames[0] #> = new SelectList(db.<#= property.EntitySetName #>, "<#= property.PrimaryKeyNames[0] #>", "<#= property.DisplayPropertyName #>", <#= ModelVariable #>.<#= property.ForeignKeyPropertyNames[0] #>);
<# } #>
return View(<#= ModelVariable #>);
}
// GET: <#= routePrefix #>/Delete/5
<# if (UseAsync) { #>
public async Task<ActionResult> Delete(<#= primaryKeyNullableTypeName #> id)
<# } else { #>
public ActionResult Delete(<#= primaryKeyNullableTypeName #> id)
<# } #>
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
<# if (UseAsync) { #>
<#= ModelTypeName #> <#= ModelVariable #> = await db.<#= entitySetName #>.FindAsync(id);
<# } else { #>
<#= ModelTypeName #> <#= ModelVariable #> = db.<#= entitySetName #>.Find(id);
<# } #>
if (<#= ModelVariable #> == null)
{
return HttpNotFound();
}
return View(<#= ModelVariable #>);
}
// POST: <#= routePrefix #>/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
<# if (UseAsync) { #>
public async Task<ActionResult> DeleteConfirmed(<#= primaryKeyShortTypeName #> id)
<# } else { #>
public ActionResult DeleteConfirmed(<#= primaryKeyShortTypeName #> id)
<# } #>
{
<# if (UseAsync) { #>
<#= ModelTypeName #> <#= ModelVariable #> = await db.<#= entitySetName #>.FindAsync(id);
<# } else { #>
<#= ModelTypeName #> <#= ModelVariable #> = db.<#= entitySetName #>.Find(id);
<# } #>
db.<#= entitySetName #>.Remove(<#= ModelVariable #>);
<# if (UseAsync) { #>
await db.SaveChangesAsync();
<# } else { #>
db.SaveChanges();
<# } #>
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
}
<#+
// This function converts the primary key short type name to its nullable equivalent when possible. This is required to make
// sure that an HTTP 400 error is thrown when the user tries to access the edit, delete, or details action with null values.
string GetNullableTypeName(string typeName, string shortTypeName)
{
// The exceptions are caught because if for any reason the type is user defined, then the short type name will be used.
// In that case the user will receive a server error if null is passed to the edit, delete, or details actions.
Type primaryKeyType = null;
try
{
primaryKeyType = Type.GetType(typeName);
}
catch
{
}
if (primaryKeyType != null && (primaryKeyType.IsPrimitive || IsGuid(typeName)))
{
return shortTypeName + "?";
}
return shortTypeName;
}
bool IsGuid(string typeName) {
return String.Equals("System.Guid", typeName, StringComparison.OrdinalIgnoreCase);
}
bool IsString(string typeName) {
return String.Equals("System.String", typeName, StringComparison.OrdinalIgnoreCase);
}
bool IsDateTime(string typeName) {
return String.Equals("System.DateTime", typeName, StringComparison.OrdinalIgnoreCase);
}
bool IsDecimal(string typeName) {
return String.Equals("System.Decimal", typeName, StringComparison.OrdinalIgnoreCase);
}
bool IsInt(string typeName) {
return String.Equals("System.Int32", typeName, StringComparison.OrdinalIgnoreCase) || String.Equals("System.Integer", typeName, StringComparison.OrdinalIgnoreCase) || String.Equals("System.Int64", typeName, StringComparison.OrdinalIgnoreCase);
}
bool IsBool(string typeName) {
return String.Equals("System.Boolean", typeName, StringComparison.OrdinalIgnoreCase);
}
RelatedModelMetadata GetRelatedModelMetadata(PropertyMetadata property){
RelatedModelMetadata propertyModel;
IDictionary<string, RelatedModelMetadata> relatedProperties;
if(ModelMetadata.RelatedEntities != null)
{
relatedProperties = ModelMetadata.RelatedEntities.ToDictionary(item => item.AssociationPropertyName);
}
else
{
relatedProperties = new Dictionary<string, RelatedModelMetadata>();
}
relatedProperties.TryGetValue(property.PropertyName, out propertyModel);
return propertyModel;
}
// A foreign key, e.g. CategoryID, will have an association name of Category
string GetAssociationName(PropertyMetadata property) {
RelatedModelMetadata propertyModel = GetRelatedModelMetadata(property);
return propertyModel != null ? propertyModel.AssociationPropertyName : property.PropertyName;
}
// A foreign key, e.g. CategoryID, will have a value expression of Category.CategoryID
string GetValueExpression(PropertyMetadata property) {
RelatedModelMetadata propertyModel = GetRelatedModelMetadata(property);
return propertyModel != null ? propertyModel.AssociationPropertyName + "." + propertyModel.DisplayPropertyName : property.PropertyName;
}
// A foreign key, e.g. CategoryID, will have a value expression of Category.CategoryID
bool HasValueExpression(PropertyMetadata property) {
RelatedModelMetadata propertyModel = GetRelatedModelMetadata(property);
return propertyModel != null;
}
// This will return the primary key property name, if and only if there is exactly
// one primary key. Returns null if there is no PK, or the PK is composite.
string GetPrimaryKeyName() {
return (ModelMetadata.PrimaryKeys != null && ModelMetadata.PrimaryKeys.Count() == 1) ? ModelMetadata.PrimaryKeys[0].PropertyName : null;
}
#>
When adding a object with a foreign key relationship, some of the generated integration tests for the API will break. Need to harden these tests generators to make sure they will still work.
DbName being used instead of DbContext in api scaffolding
Could you kindly add the table column type ?
Thank you in advance.
Abdoessalam
My suggestion is :
To add Column type property - the column attribute in EF core is as follows :
Column Attribute: [Column (string name, Properties:[Order = int],[TypeName = string])
ex
[Column("DoB", Order = 1, TypeName="DateTime2")]
The suggested steps to add it :
a- open Craftsman\Models\EntityProperty.cs file, append the following property:
public string ColumnType { get; set; }
b- open Craftsman\Builders\EntityBuilder.cs file and replace the following:
if (!string.IsNullOrEmpty(entityProperty.ColumnName))
attributeString += @$" [Column(""{entityProperty.ColumnName}"",TypeName=""{entityProperty.ColumnType}"")]{Environment.NewLine}";
with the following:
if (!string.IsNullOrEmpty(entityProperty.ColumnName))
{
if (!string.IsNullOrEmpty(entityProperty.ColumnType))
attributeString += @$" [Column(""{entityProperty.ColumnName}"",TypeName=""{entityProperty.ColumnType}"")]{Environment.NewLine}";
else
attributeString += @$" [Column(""{entityProperty.ColumnName}"")]{Environment.NewLine}";
}
I find it a bit difficult to translate what Wrapt is doing when compared to the example repo that Jimmy Bogard made: https://github.com/jbogard/ContosoUniversityDotNetCore-Pages
It would be nice to see your take on the Contoso University example using a WebAPI with Wrapt and eventually adding in a SPA front end as well.
if i submit a PUT with an object identical to the one in the db, the save will not succeed because EF will not do anything to it. need to add a check to see if the object is identical and, if so, return an OK?
Is this tool only intended for windows?
I am already getting errors when running the craftsman new:api command.
craftsman new:api $HOME/projects/work/template.yaml Your template file was parsed successfully. An unhandled exception occured when running the API command. The error details are: The
/home/user/projects/work\Template.Api/Domain\Entitiesdirectory could not be found.
Functional Test Utilities not getting added for non-auth templates
Hi,
Are you changing how to deal with primary keys?
No problems with running craftsman version 0.11.1
but, will produce the following error when run on a clone of dev branch, Am I missing something?
YamlException: (Line: 39, Col: 7, Idx: 1089) - (Line: 39, Col: 7, Idx: 1089): Exception during deserialization
SerializationException: Property 'IsPrimaryKey' not found on type 'Craftsman.Models.EntityProperty'.
at GetProperty(Type type, Object container, String name, Boolean ignoreUnmatched)
at GetProperty(Type type, Object container, String name, Boolean ignoreUnmatched)
at Deserialize(IParser parser, Type expectedType, Func`3 nestedObjectDeserializer, Object& value)
at DeserializeValue(IParser parser, Type expectedType, SerializerState state, IValueDeserializer nestedObjectDeserializer)
....
.....
Hi, I could not override the primary key Id property of the BaseEntity class.
Is there a way to use our natural primary keys when available. Or better yet, can you change it to generic "BaseEntity"?
Best regards,
Abdoessalam
Thanks for this fantastic project! I'm trying out Craftsman for the first time following https://wrapt.dev/docs/installation The CLI installs successfully but errors out when invoked.
โฏ dotnet tool install -g craftsman
You can invoke the tool using the following command: craftsman
Tool 'craftsman' (version '0.12.2') was successfully installed.
โฏ craftsman
It was not possible to find any compatible framework version
The framework 'Microsoft.AspNetCore.App', version '5.0.0' (arm64) was not found.
- The following frameworks were found:
6.0.1 at [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
You can resolve the problem by installing the specified framework and/or SDK.
The specified framework can be found at:
- https://aka.ms/dotnet-core-applaunch?framework=Microsoft.AspNetCore.App&framework_version=5.0.0&arch=arm64&rid=osx.12-arm64
โฏ echo $?
150
โฏ craftsman list
It was not possible to find any compatible framework version
The framework 'Microsoft.AspNetCore.App', version '5.0.0' (arm64) was not found.
- The following frameworks were found:
6.0.1 at [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
You can resolve the problem by installing the specified framework and/or SDK.
The specified framework can be found at:
- https://aka.ms/dotnet-core-applaunch?framework=Microsoft.AspNetCore.App&framework_version=5.0.0&arch=arm64&rid=osx.12-arm64
โฏ dotnet tool update -g craftsman
Tool 'craftsman' was reinstalled with the latest stable version (version '0.12.2').
Environment info
โฏ dotnet --info
.NET SDK (reflecting any global.json):
Version: 6.0.101
Commit: ef49f6213a
Runtime Environment:
OS Name: Mac OS X
OS Version: 12.1
OS Platform: Darwin
RID: osx.12-arm64
Base Path: /usr/local/share/dotnet/sdk/6.0.101/
Host (useful for support):
Version: 6.0.1
Commit: 3a25a7f1cc
.NET SDKs installed:
6.0.101 [/usr/local/share/dotnet/sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 6.0.1 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 6.0.1 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
To install additional .NET runtimes or SDKs:
https://aka.ms/dotnet-download
Dependencies are working as expected:
โฏ dotnet tool list --global
Package Id Version Commands
--------------------------------------
craftsman 0.12.2 craftsman
dotnet-ef 6.0.1 dotnet-ef
~/Downloads
โฏ dotnet ef --version
Entity Framework Core .NET Command-line Tools
6.0.1
~/Downloads
โฏ docker --version
Docker version 20.10.11, build dea9396
Dotnet 6 is the only version with native arm64 support for M1 mac. So i do not have dotnet 5 installed.
I created a small web API template with dotnet 6 to verify that it successfully works as expected. How do I get past this error?
The generated code is still using the old startup.cs/program.cs format. I was having problems when deploying to azure with serilog, after converting to the new program.cs format I was able to successfully deploy to azure. Below is the new program.cs file, it would be great if this can be done when the code is generated
using Autofac.Extensions.DependencyInjection;
using Test.Api.Databases;
using Test.Api.Extensions.Application;
using Test.Api.Extensions.Services;
using Test.Api.Seeders.DummyData;
using Serilog;
using System.Text.Json.Serialization;
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog((ctx, lc) => lc
.WriteTo.Console()
.WriteTo.Seq("http://localhost:5341"));
Log.Information("Starting application");
var services = builder.Services;
services.AddSingleton(Log.Logger);
// TODO update CORS for your env
services.AddCorsService("Test.ApiCorsPolicy", builder.Environment);
services.AddInfrastructure(builder.Configuration, builder.Environment);
services.AddControllers()
.AddJsonOptions(o => o.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);
services.AddApiVersioningExtension();
services.AddWebApiServices();
services.AddHealthChecks();
// Dynamic Services
services.AddSwaggerExtension(builder.Configuration);
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
var serviceScopeFactory = app.Services.GetService<IServiceScopeFactory>();
using (var scope = serviceScopeFactory.CreateScope())
{
var context = (ApplicationDbContext)scope.ServiceProvider.GetService(typeof(ApplicationDbContext));
context.Database.EnsureCreated();
BlogPostSeeder.SeedSampleBlogPostData(context);
BlogCategorySeeder.SeedSampleBlogCategoryData(context);
BlogTagSeeder.SeedSampleBlogTagData(context);
UserSeeder.SeedSampleUserData(context);
SubscriptionSeeder.SeedSampleSubscriptionData(context);
ContactFormSeeder.SeedSampleContactFormData(context);
}
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
// For elevated security, it is recommended to remove this middleware and set your server to only listen on https.
// A slightly less secure option would be to redirect http to 400, 505, etc.
app.UseHttpsRedirection();
app.UseCors("Test.ApiCorsPolicy");
app.UseSerilogRequestLogging();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/api/health");
endpoints.MapControllers();
});
// Dynamic App
app.UseSwaggerExtension(builder.Configuration);
app.Run();
public partial class Program { } // --> Needed for Functional Unit Test
I'd love to contribute this change, if this is something that sounds interesting to you!
When using Auth, the API will run fine, but the integration tests will break, throwing a -------- System.InvalidOperationException : Scheme already exists: Identity.Application
error.
I started googling for this and it seems like the main resolution is generally to remove AddDefaultIdentity
to either stop a clash with IdentityHostingStartup
or prevent IdentityHostintgStartup.cs from causing some overlap.
I'm not using AddDefaultIdentity and I'm not seeing a IdentityHostintgStartup.cs get generated, so I'm not quite sure what the deal is here. Presumably, something is calling AddAuthentication
with the same identity scheme twice. Some minor discussion is here. This may be be due to CustomWebApplicationFactory
running through startup multiple times, but I need to investigate more.
It does look like, when debugging any integration test that services.AddIdentity<ApplicationUser, IdentityRole>().AddEntityFrameworkStores<IdentityDbContext>().AddDefaultTokenProviders();
is getting hit twice and, when commenting that line out, I get a different error: -------- System.InvalidOperationException : Scheme already exists: Bearer
which, agian, is presumably happening because of startup getting run twice in CustomWebApplicationFactory
.
I need to really dive into this subject more and figure out a good resolution and workflow for this as this has happened to me with numerous other services in the past.
@JuanCarbo I just started a write up for this and when I went to try it in my CLI (i.e. craftsman new:api ..\ApiTemplateName.yaml
) and it worked just fine. Could you confirm that it still doesn't work for you?
The Name annotation will only be produced for get action [HttpGet(Name = "GetAddresss")] but not for other actions in the controller.
Which will produce generic methods for some client generators and errors for the others.
[HttpGet(Name = "GetAddresss")] -> Ok
[HttpPost] without the name property -> not ok
Creating a template file by copying the one provided in tutorial section "Environment and Swagger Setup", and even placing it in the same directory I run the command craftsman new:domain
from leads to the following output:
No such file or directory
An unhandled exception occurred when running the API command.
The error details are:
No such file or directory
Nevertheless the following empty directory structure gets created:
CarbonKitchen
โโโ RecipeManagement
โโโ src
โโโ tests
Craftsman version (craftsman version
):
v0.9.3.0
Need to update the scaffolding to better accommodate foreign keys that can be filtered
Hi,
Is there nightly builds available to try some added feature?
Thank you.
Abdoessalam
This isn't a bug in craftsman, but in a library used in wrapt projects.
Autobogus can not generate fakes on entities using DateOnly
or TimeOnly
. This is because Bogus did not support it. They have since released 34.x which provides this support, but autobogus needs to bring it in to their package. You can track the progress of it here.
The serialization of a patch doc in the functional tests is breaking one of the PATCH tests.
Hello I am using craftsman v0.13.1. Looking at the current code base. It looks like support for AuthorizationSettings has been removed as there is no class for it in the models directory. Is this true? Has the Authorization functionality been removed entirely? Reading the docs indicates that the framework should support it.
https://wrapt.dev/docs/authorization-settings#authorization-settings-properties
Any actions of initializing new project with craftsman end up with an error:
ERROR: The /home/thestacks/source/repos/thestacks/MyExampleProject/RecipeManagement/src/Re cipeManagement/Properties/launchsettings.json
file could not be found.`
Launching command craftsman new:example
does not work too.
In my opinion this is a problem with case-sensitive file system of Linux.
The file is there, and it's called launchSettings.json not launchsettings.json
This bug is a blocker, I cannot use craftsman at all because of this error.
craftsman new:example
Craftsman version (dotnet tool list -g
): 0.14.0
When running a -h command on new:domain
like so craftsman new:domain -h
, it shows the BC help instead of new domain
Craftsman version (dotnet tool list -g
): 0.9.3
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.