diff --git a/.gitignore b/.gitignore index e81bfb9e..31f175bb 100644 --- a/.gitignore +++ b/.gitignore @@ -287,3 +287,4 @@ __pycache__/ *.odx.cs *.xsd.cs /App_Data +/Bot.WebStarter/App_Data/bot-rasa.db diff --git a/Bot.Rasa.RestApi/AgentController.cs b/Bot.Rasa.RestApi/AgentController.cs index 34843c60..25524b54 100644 --- a/Bot.Rasa.RestApi/AgentController.cs +++ b/Bot.Rasa.RestApi/AgentController.cs @@ -1,5 +1,5 @@ using Bot.Rasa.Agents; -using Bot.Rasa.Console; +using Bot.Rasa.Consoles; using EntityFrameworkCore.BootKit; using Microsoft.AspNetCore.Mvc; using System; diff --git a/Bot.Rasa.RestApi/Bot.Rasa.RestApi.csproj b/Bot.Rasa.RestApi/Bot.Rasa.RestApi.csproj index dcb02a5a..a43c3086 100644 --- a/Bot.Rasa.RestApi/Bot.Rasa.RestApi.csproj +++ b/Bot.Rasa.RestApi/Bot.Rasa.RestApi.csproj @@ -10,9 +10,8 @@ - - - + + diff --git a/Bot.Rasa.RestApi/EntityDbContextBuilderExtensions.cs b/Bot.Rasa.RestApi/EntityDbContextBuilderExtensions.cs new file mode 100644 index 00000000..9db6eb0a --- /dev/null +++ b/Bot.Rasa.RestApi/EntityDbContextBuilderExtensions.cs @@ -0,0 +1,25 @@ +using EntityFrameworkCore.BootKit; +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.AspNetCore.Builder +{ + public static class EntityDbContextBuilderExtensions + { + /// + /// Use CustomEntityFoundation + /// + /// + /// + /// + /// + public static void UseEntityDbContext(this IApplicationBuilder app, IConfiguration configuration, String contentRootPath, String[] assembles) + { + Database.Configuration = configuration; + Database.Assemblies = assembles; + Database.ContentRootPath = contentRootPath; + } + } +} diff --git a/Bot.Rasa.RestApi/EssentialController.cs b/Bot.Rasa.RestApi/EssentialController.cs index ea322c6c..c7db40c5 100644 --- a/Bot.Rasa.RestApi/EssentialController.cs +++ b/Bot.Rasa.RestApi/EssentialController.cs @@ -1,6 +1,7 @@ -using Bot.Rasa.Console; +using Bot.Rasa.Consoles; using DotNetToolkit; using EntityFrameworkCore.BootKit; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Data.Sqlite; using MySql.Data.MySqlClient; @@ -12,9 +13,7 @@ using System.Text; namespace Bot.Rasa.RestApi { -#if !DEBUG - [Authorize] -#endif + //[Authorize] [Produces("application/json")] [Route("bot/[controller]")] public class EssentialController : ControllerBase @@ -23,46 +22,7 @@ namespace Bot.Rasa.RestApi public EssentialController() { - dc = new Database(); - - string db = RasaConsole.Options.DbName; - string connectionString = RasaConsole.Options.DbConnectionString; - - if (db.Equals("SqlServer")) - { - dc.BindDbContext(new DatabaseBind - { - MasterConnection = new SqlConnection(connectionString), - CreateDbIfNotExist = true, - AssemblyNames = RasaConsole.Options.Assembles - }); - } - else if (db.Equals("Sqlite")) - { - connectionString = connectionString.Replace("|DataDirectory|\\", RasaConsole.Options.ContentRootPath + "\\App_Data\\"); - dc.BindDbContext(new DatabaseBind - { - MasterConnection = new SqliteConnection(connectionString), - CreateDbIfNotExist = true, - AssemblyNames = RasaConsole.Options.Assembles - }); - } - else if (db.Equals("MySql")) - { - dc.BindDbContext(new DatabaseBind - { - MasterConnection = new MySqlConnection(connectionString), - CreateDbIfNotExist = true, - AssemblyNames = RasaConsole.Options.Assembles - }); - } - else if (db.Equals("InMemory")) - { - dc.BindDbContext(new DatabaseBind - { - AssemblyNames = RasaConsole.Options.Assembles - }); - } + dc = new DefaultDataContextLoader().GetDefaultDc(); } [HttpPatch("{table}/{id}")] diff --git a/Bot.Rasa.RestApi/InitLoaderBuilderExtensions.cs b/Bot.Rasa.RestApi/InitLoaderBuilderExtensions.cs new file mode 100644 index 00000000..9390668e --- /dev/null +++ b/Bot.Rasa.RestApi/InitLoaderBuilderExtensions.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Text; +using Bot.Rasa.Loader; + +namespace Microsoft.AspNetCore.Builder +{ + public static class InitLoaderBuilderExtensions + { + /// + /// Initialize Loader + /// + /// + public static void UseInitLoader(this IApplicationBuilder app) + { + new InitializationLoader().Load(); + } + } +} diff --git a/Bot.Rasa/Bot.Rasa.csproj b/Bot.Rasa/Bot.Rasa.csproj index 1aa114b2..6528de66 100644 --- a/Bot.Rasa/Bot.Rasa.csproj +++ b/Bot.Rasa/Bot.Rasa.csproj @@ -6,9 +6,10 @@ - - - + + + + diff --git a/Bot.Rasa/Console/RasaConsole.cs b/Bot.Rasa/Consoles/RasaConsole.cs similarity index 98% rename from Bot.Rasa/Console/RasaConsole.cs rename to Bot.Rasa/Consoles/RasaConsole.cs index 04331388..41d8115f 100644 --- a/Bot.Rasa/Console/RasaConsole.cs +++ b/Bot.Rasa/Consoles/RasaConsole.cs @@ -11,7 +11,7 @@ using System.IO; using System.Linq; using System.Text; -namespace Bot.Rasa.Console +namespace Bot.Rasa.Consoles { public class RasaConsole { diff --git a/Bot.Rasa/Console/RasaOptions.cs b/Bot.Rasa/Consoles/RasaOptions.cs similarity index 92% rename from Bot.Rasa/Console/RasaOptions.cs rename to Bot.Rasa/Consoles/RasaOptions.cs index 81456305..75b9e1bc 100644 --- a/Bot.Rasa/Console/RasaOptions.cs +++ b/Bot.Rasa/Consoles/RasaOptions.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace Bot.Rasa.Console +namespace Bot.Rasa.Consoles { public class RasaOptions { diff --git a/Bot.Rasa/Console/RequestExtension.cs b/Bot.Rasa/Consoles/RequestExtension.cs similarity index 98% rename from Bot.Rasa/Console/RequestExtension.cs rename to Bot.Rasa/Consoles/RequestExtension.cs index 0d35244b..4ea225c7 100644 --- a/Bot.Rasa/Console/RequestExtension.cs +++ b/Bot.Rasa/Consoles/RequestExtension.cs @@ -9,7 +9,7 @@ using System.Collections.Generic; using System.IO; using System.Text; -namespace Bot.Rasa.Console +namespace Bot.Rasa.Consoles { public static class RequestExtension { diff --git a/Bot.Rasa/Loader/DbInitializer.cs b/Bot.Rasa/Loader/DbInitializer.cs new file mode 100644 index 00000000..9b311eff --- /dev/null +++ b/Bot.Rasa/Loader/DbInitializer.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; +using EntityFrameworkCore.BootKit; +using DotNetToolkit; + +namespace Bot.Rasa.Loader +{ + public class DbInitializer : IInitializationLoader + { + public int Priority => 1; + + public void Initialize() + { + var dc = new DefaultDataContextLoader().GetDefaultDc(); + + var instances = TypeHelper.GetInstanceWithInterface(Database.Assemblies).OrderBy(x => x.Priority).ToList(); + + for (int idx = 0; idx < instances.Count; idx++) + { + DateTime start = DateTime.UtcNow; + Console.WriteLine($"{instances[idx].ToString()} P:{instances[idx].Priority} started at {DateTime.UtcNow}"); + int effected = dc.DbTran(() => instances[idx].Load(Database.Configuration, dc)); + Console.WriteLine($"{instances[idx].ToString()} effected [{effected}] records in {(DateTime.UtcNow - start).TotalMilliseconds} ms"); + } + } + } +} diff --git a/Bot.Rasa/Loader/IHookDbInitializer.cs b/Bot.Rasa/Loader/IHookDbInitializer.cs new file mode 100644 index 00000000..420df8ea --- /dev/null +++ b/Bot.Rasa/Loader/IHookDbInitializer.cs @@ -0,0 +1,27 @@ +using EntityFrameworkCore.BootKit; +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bot.Rasa.Loader +{ + /// + /// Initialize data for modules + /// + public interface IHookDbInitializer + { + /// + /// value smaller is higher priority + /// + int Priority { get; } + + /// + /// Load data + /// + /// + /// + /// + void Load(IConfiguration config, Database dc); + } +} diff --git a/Bot.Rasa/Loader/IInitializationLoader.cs b/Bot.Rasa/Loader/IInitializationLoader.cs new file mode 100644 index 00000000..746c2c92 --- /dev/null +++ b/Bot.Rasa/Loader/IInitializationLoader.cs @@ -0,0 +1,13 @@ +using System.Text; + +namespace Bot.Rasa.Loader +{ + /// + /// Implement a customzied loader + /// + public interface IInitializationLoader + { + int Priority { get; } + void Initialize(); + } +} diff --git a/Bot.Rasa/Loader/InitializationLoader.cs b/Bot.Rasa/Loader/InitializationLoader.cs new file mode 100644 index 00000000..153f4a64 --- /dev/null +++ b/Bot.Rasa/Loader/InitializationLoader.cs @@ -0,0 +1,34 @@ +using DotNetToolkit; +using EntityFrameworkCore.BootKit; +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bot.Rasa.Loader +{ + public class InitializationLoader + { + public void Load() + { + DateTime startAll = DateTime.UtcNow; + Console.WriteLine(); + Console.WriteLine($"*** *** *** *** InitializationLoader running *** *** *** ***"); + Console.WriteLine(); + + var coreLoaders = TypeHelper.GetInstanceWithInterface(Database.Assemblies); + + coreLoaders.ForEach(loader => + { + DateTime start = DateTime.UtcNow; + Console.WriteLine($"{loader.ToString()} P:{loader.Priority}..."); + loader.Initialize(); + Console.WriteLine($"{loader.ToString()} completed in {(DateTime.UtcNow - start).TotalSeconds} s."); + Console.WriteLine(); + }); + + Console.WriteLine($"*** *** *** *** InitializationLoader completed in {(DateTime.UtcNow - startAll).TotalSeconds} s. *** *** *** ***"); + Console.WriteLine(); + } + } +} diff --git a/Bot.UnitTest/AgentTest.cs b/Bot.UnitTest/AgentTest.cs index 8c7597b9..3b8319c5 100644 --- a/Bot.UnitTest/AgentTest.cs +++ b/Bot.UnitTest/AgentTest.cs @@ -1,6 +1,6 @@ using Bot.Rasa; using Bot.Rasa.Agents; -using Bot.Rasa.Console; +using Bot.Rasa.Consoles; using EntityFrameworkCore.BootKit; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; diff --git a/Bot.UnitTest/Bot.UnitTest.csproj b/Bot.UnitTest/Bot.UnitTest.csproj index 020eafdb..4978c4ec 100644 --- a/Bot.UnitTest/Bot.UnitTest.csproj +++ b/Bot.UnitTest/Bot.UnitTest.csproj @@ -7,7 +7,9 @@ - + + + diff --git a/Bot.UnitTest/TestEssential.cs b/Bot.UnitTest/TestEssential.cs index ee839977..5d633b31 100644 --- a/Bot.UnitTest/TestEssential.cs +++ b/Bot.UnitTest/TestEssential.cs @@ -1,21 +1,39 @@ -using Bot.Rasa.Console; +using Bot.Rasa.Consoles; using EntityFrameworkCore.BootKit; using Microsoft.Data.Sqlite; +using Microsoft.Extensions.Configuration; using System; using System.IO; +using System.Linq; namespace Bot.UnitTest { public abstract class TestEssential { protected Database dc { get; set; } + protected string contentRoot; public TestEssential() { + contentRoot = $"{Directory.GetCurrentDirectory()}\\..\\..\\..\\..\\Bot.WebStarter"; + + ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); + var settings = Directory.GetFiles(contentRoot, "settings.*.json"); + settings.ToList().ForEach(setting => + { + configurationBuilder.AddJsonFile(setting, optional: false, reloadOnChange: true); + }); + Database.Configuration = configurationBuilder.Build(); + + Database.Assemblies = new String[] { "Bot.Rasa" }; + Database.ContentRootPath = contentRoot; + + dc = new DefaultDataContextLoader().GetDefaultDc(); + RasaConsole.Options = new RasaOptions { HostUrl = "http://192.168.56.101:5000", - ContentRootPath = $"{Directory.GetCurrentDirectory()}\\..\\..\\..\\..\\Bot.WebStarter", + ContentRootPath = contentRoot, Assembles = new String[] { "Bot.Rasa" } }; @@ -24,8 +42,7 @@ namespace Bot.UnitTest dc.BindDbContext(new DatabaseBind { MasterConnection = new SqliteConnection($"Data Source={RasaConsole.Options.ContentRootPath}\\App_Data\\bot-rasa.db"), - CreateDbIfNotExist = true, - AssemblyNames = new string[] { "Bot.Rasa" } + CreateDbIfNotExist = true }); } } diff --git a/Bot.WebStarter/Bot.WebStarter.csproj b/Bot.WebStarter/Bot.WebStarter.csproj index 6c62ba3a..cb56ae3a 100644 --- a/Bot.WebStarter/Bot.WebStarter.csproj +++ b/Bot.WebStarter/Bot.WebStarter.csproj @@ -13,7 +13,10 @@ - + + + + diff --git a/Bot.WebStarter/Program.cs b/Bot.WebStarter/Program.cs index 56a33126..0e164648 100644 --- a/Bot.WebStarter/Program.cs +++ b/Bot.WebStarter/Program.cs @@ -19,6 +19,16 @@ namespace Bot.WebStarter public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) + .ConfigureAppConfiguration((hostingContext, config) => + { + var env = hostingContext.HostingEnvironment; + var settings = Directory.GetFiles("./", "settings.*.json"); + settings.ToList().ForEach(setting => + { + config.AddJsonFile(setting, optional: false, reloadOnChange: true); + }); + }) + .UseUrls("http://localhost:9900") .UseStartup() .Build(); } diff --git a/Bot.WebStarter/Startup.cs b/Bot.WebStarter/Startup.cs index 5dee6b04..bacfce11 100644 --- a/Bot.WebStarter/Startup.cs +++ b/Bot.WebStarter/Startup.cs @@ -1,14 +1,19 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; -using Bot.Rasa.Console; +using Bot.Rasa.Consoles; +using DotNetToolkit.JwtHelper; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Microsoft.Extensions.PlatformAbstractions; +using Newtonsoft.Json.Serialization; +using Swashbuckle.AspNetCore.Swagger; namespace Bot.WebStarter { @@ -24,7 +29,32 @@ namespace Bot.WebStarter // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { + services.AddCors(); + services.AddJwtAuth(Configuration); + services.AddMvc(); + + // Add framework services. + services.AddMvc(options => + { + options.RespectBrowserAcceptHeader = true; + }).AddJsonOptions(options => + { + options.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()); + options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + //options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore; + }); + + services.AddSwaggerGen(c => + { + c.AddSecurityDefinition("Bearer", new ApiKeyScheme() { In = "header", Description = "Please insert JWT with Bearer into field", Name = "Authorization", Type = "apiKey" }); + + var info = Configuration.GetSection("Swagger").Get(); + c.SwaggerDoc(info.Version, info); + + var filePath = Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, "Bot.Rasa.RestApi.xml"); + c.IncludeXmlComments(filePath); + }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -35,17 +65,24 @@ namespace Bot.WebStarter app.UseDeveloperExceptionPage(); } + app.UseStaticFiles(); + + app.UseSwagger(); + app.UseSwaggerUI(c => + { + c.SupportedSubmitMethods(SubmitMethod.Get, SubmitMethod.Post, SubmitMethod.Put, SubmitMethod.Patch, SubmitMethod.Delete); + c.ShowExtensions(); + c.SwaggerEndpoint("/swagger/v1/swagger.json", "API v1"); + c.RoutePrefix = "api"; + }); + + app.UseCors(builder => builder.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().AllowCredentials()); + + app.UseAuthentication(); app.UseMvc(); - string db = RasaConsole.Configuration.GetSection("Database:Default").Value; - RasaConsole.Options = new RasaOptions - { - HostUrl = Configuration.GetSection("Rasa:Host").Value, - Assembles = new String[] { "Bot.Rasa" }, - ContentRootPath = env.ContentRootPath, - DbName = db, - DbConnectionString = Configuration.GetSection("Database:ConnectionStrings")[db] - }; + app.UseEntityDbContext(Configuration, env.ContentRootPath, new String[] { "Bot.Rasa" }); + app.UseInitLoader(); } } } diff --git a/Bot.WebStarter/Settings/appsettings.json b/Bot.WebStarter/settings.app.json similarity index 78% rename from Bot.WebStarter/Settings/appsettings.json rename to Bot.WebStarter/settings.app.json index 287e2813..26bb0ac7 100644 --- a/Bot.WebStarter/Settings/appsettings.json +++ b/Bot.WebStarter/settings.app.json @@ -11,9 +11,5 @@ "Default": "Warning" } } - }, - - "Rasa": { - "Host": "http://192.168.56.101:5000" } } diff --git a/Bot.WebStarter/settings.auth.json b/Bot.WebStarter/settings.auth.json new file mode 100644 index 00000000..e02ee249 --- /dev/null +++ b/Bot.WebStarter/settings.auth.json @@ -0,0 +1,11 @@ +{ + "TokenAuthentication": { + "SecretKey": "tK5aNR0FTEm4htInv+HA3A==", + "Subject": "Voicecoin", + "Issuer": "Voicecoin", + "Audience": "Voicecoin", + "TokenPath": "/token", + "CookieName": "token", + "LoginPath": "/login" + } +} \ No newline at end of file diff --git a/Bot.WebStarter/settings.aws.json b/Bot.WebStarter/settings.aws.json new file mode 100644 index 00000000..6336e421 --- /dev/null +++ b/Bot.WebStarter/settings.aws.json @@ -0,0 +1,10 @@ +{ + "AWS": { + "AWSRegionEndPoint": "us-east-1", + "AWSSecretKey": "38+UqlSqOVbKfL3LrS6hSmgW/ZSPgZzUa/Hom7ip", + "AWSAccessKey": "AKIAIPXFRDXOTKZWOVXQ", + "AWSEncoding": "utf-8", + "SESVerifiedEmail": "haiping008@gmail.com", + "AWSBucketPrefix": "voicecoin.ico" + } +} \ No newline at end of file diff --git a/Bot.WebStarter/Settings/dbsettings.json b/Bot.WebStarter/settings.db.json similarity index 100% rename from Bot.WebStarter/Settings/dbsettings.json rename to Bot.WebStarter/settings.db.json diff --git a/Bot.WebStarter/settings.json b/Bot.WebStarter/settings.json new file mode 100644 index 00000000..e69de29b diff --git a/Bot.WebStarter/settings.rasa.json b/Bot.WebStarter/settings.rasa.json new file mode 100644 index 00000000..f1bb10fb --- /dev/null +++ b/Bot.WebStarter/settings.rasa.json @@ -0,0 +1,5 @@ +{ + "Rasa": { + "Host": "http://192.168.56.101:5000" + } +} diff --git a/Bot.WebStarter/settings.swagger.json b/Bot.WebStarter/settings.swagger.json new file mode 100644 index 00000000..b5a580a8 --- /dev/null +++ b/Bot.WebStarter/settings.swagger.json @@ -0,0 +1,12 @@ +{ + "Swagger": { + "Version": "v1", + "Title": "RasaBot API", + "Description": "RasaBot API", + "TermsOfService": "MIT", + "Contact": { + "Name": "Haiping Chen", + "Email": "haiping008@gmail.com" + } + } +}