diff --git a/devdocs/todo.txt b/devdocs/todo.txt
index 8ec6dca4..70536456 100644
--- a/devdocs/todo.txt
+++ b/devdocs/todo.txt
@@ -8,6 +8,8 @@ Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOiIxNTYxNDk4NzQ4IiwiZXhwIjoi
UPDATE all the things before commencing work
- https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.0&tabs=visual-studio-code
- https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/breaking-changes
+ - https://github.com/domaindrivendev/Swashbuckle.AspNetCore#swashbuckleaspnetcoreannotations
+ -
Need a sprint to get to a fully testable client with entry form, list and as much as possible all features from COMMON-* specs list
Do the stuff in the Client todo first then back to the server as required.
diff --git a/server/AyaNova/AyaNova.csproj b/server/AyaNova/AyaNova.csproj
index 7f4df66e..42178ba8 100644
--- a/server/AyaNova/AyaNova.csproj
+++ b/server/AyaNova/AyaNova.csproj
@@ -31,13 +31,23 @@
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
diff --git a/server/AyaNova/Startup.cs b/server/AyaNova/Startup.cs
index 87d62559..5397aaf8 100644
--- a/server/AyaNova/Startup.cs
+++ b/server/AyaNova/Startup.cs
@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+
using Microsoft.Extensions.Logging;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authentication.JwtBearer;
@@ -11,6 +12,9 @@ using Microsoft.IdentityModel.Tokens;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Http;
+using Microsoft.OpenApi.Models;
+
+
using AyaNova.Models;
using AyaNova.Util;
using AyaNova.Generator;
@@ -24,7 +28,8 @@ using System.IO;
using System.Reflection;
using System.Linq;
using System;
-
+using System.Collections.Generic;
+using Newtonsoft.Json.Serialization;
namespace AyaNova
@@ -36,7 +41,7 @@ namespace AyaNova
/////////////////////////////////////////////////////////////
//
- public Startup(ILogger logger, ILoggerFactory logFactory, Microsoft.AspNetCore.Hosting.IHostingEnvironment hostingEnvironment)
+ public Startup(ILogger logger, ILoggerFactory logFactory, Microsoft.AspNetCore.Hosting.IWebHostEnvironment hostingEnvironment)
{
_log = logger;
_hostingEnvironment = hostingEnvironment;
@@ -48,7 +53,7 @@ namespace AyaNova
private readonly ILogger _log;
private string _connectionString = "";
- private readonly Microsoft.AspNetCore.Hosting.IHostingEnvironment _hostingEnvironment;
+ private readonly Microsoft.AspNetCore.Hosting.IWebHostEnvironment _hostingEnvironment;
////////////////////////////////////////////////////////////
// This method gets called by the runtime. Use this method to add services to the container.
@@ -57,14 +62,23 @@ namespace AyaNova
{
_log.LogDebug("BOOT: initializing services...");
+
+
//Server state service for shutting people out of api
_log.LogDebug("BOOT: init ApiServerState service");
services.AddSingleton(new AyaNova.Api.ControllerHelpers.ApiServerState());
+ //Init mvc
+ _log.LogDebug("BOOT: init MVC Core service");
+ var mvc = services.AddMvcCore();
+ _log.LogDebug("BOOT: add json service");
+ mvc.AddNewtonsoftJson();
+
+
// add the versioned api explorer, which also adds IApiVersionDescriptionProvider service
// note: the specified format code will format the version as "'v'major[.minor][-status]"
_log.LogDebug("BOOT: init ApiExplorer service");
- services.AddMvcCore().AddVersionedApiExplorer(o => o.GroupNameFormat = "'v'VVV");
+ services.AddVersionedApiExplorer(o => o.GroupNameFormat = "'v'VVV");
_log.LogDebug("BOOT: ensuring user and backup folders exist and are separate locations...");
FileUtil.EnsureUserAndUtilityFoldersExistAndAreNotIdentical(_hostingEnvironment.ContentRootPath);
@@ -98,7 +112,7 @@ namespace AyaNova
bool LOG_SENSITIVE_DATA = false;
#if (DEBUG)
- // LOG_SENSITIVE_DATA = true;
+ // LOG_SENSITIVE_DATA = true;
#endif
@@ -110,7 +124,8 @@ namespace AyaNova
)//http://www.npgsql.org/efcore/misc.html?q=execution%20strategy#execution-strategy
.ConfigureWarnings(warnings => //https://livebook.manning.com/#!/book/entity-framework-core-in-action/chapter-12/v-10/85
warnings.Throw( //Throw an exception on client eval, not necessarily an error but a smell
- Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.QueryClientEvaluationWarning))
+ // Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.QueryClientEvaluationWarning
+ ))
.EnableSensitiveDataLogging(LOG_SENSITIVE_DATA)
);
@@ -154,7 +169,9 @@ namespace AyaNova
}).AddMetrics().AddJsonOptions(options =>
{
- options.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
+ //2019-10-15 - removed this due to fuckery after update, is it required??? Not sure at this point
+ //if metrics have wrong dates then it's important I guess
+ //options.JsonSerializerOptions.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
});
@@ -179,29 +196,55 @@ namespace AyaNova
}
// add a custom operation filter which sets default values
- c.OperationFilter();
+ //Removed because will no longer compile the SwaggerDefaultValues but may be needed, not sure it's all a bit muddled
+ // c.OperationFilter();
// integrate xml comments
c.IncludeXmlComments(XmlCommentsFilePath);
+ //2019-10-15 - Removed this because apikeyscheme is no longer recognized and it appears it *may* not be necessary... TWT
+ //If I have any issues with bearer tokens in swagger then this is probably necessary but in a new way
//this is required to allow authentication when testing secure routes via swagger UI
- c.AddSecurityDefinition("Bearer", new ApiKeyScheme
- {
- Description = "JWT Authorization header using the Bearer scheme. Get your token by logging in via the Auth route then enter it here with the \"Bearer \" prefix. Example: \"Bearer {token}\"",
- Name = "Authorization",
- In = "header",
- Type = "apiKey"
+ // c.AddSecurityDefinition("Bearer", new ApiKeyScheme
+ // {
+ // Description = "JWT Authorization header using the Bearer scheme. Get your token by logging in via the Auth route then enter it here with the \"Bearer \" prefix. Example: \"Bearer {token}\"",
+ // Name = "Authorization",
+ // In = "header",
+ // Type = "apiKey"
- });
+ // });
- c.AddSecurityRequirement(new System.Collections.Generic.Dictionary>
+
+
+ //Obsolete way
+ // c.AddSecurityRequirement(new System.Collections.Generic.Dictionary>
+ // {
+ // { "Bearer", new string[] { } }
+ // });
+
+ //https://stackoverflow.com/questions/56234504/migrating-to-swashbuckle-aspnetcore-version-5
+ //First we define the security scheme
+ c.AddSecurityDefinition("Bearer", //Name the security scheme
+ new OpenApiSecurityScheme
{
- { "Bearer", new string[] { } }
+ Description = "JWT Authorization header using the Bearer scheme.",
+ Type = SecuritySchemeType.Http, //We set the scheme type to http since we're using bearer authentication
+ Scheme = "bearer" //The name of the HTTP Authorization scheme to be used in the Authorization header. In this case "bearer".
});
+ c.AddSecurityRequirement(new OpenApiSecurityRequirement{
+ {
+ new OpenApiSecurityScheme{
+ Reference = new OpenApiReference{
+ Id = "Bearer", //The name of the previously defined security scheme.
+ Type = ReferenceType.SecurityScheme
+ }
+ },new List()
+ }
+ });
});
@@ -275,7 +318,7 @@ namespace AyaNova
////////////////////////////////////////////////////////////
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
//
- public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHostingEnvironment env,
+ public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IWebHostEnvironment env,
AyContext dbContext, IApiVersionDescriptionProvider provider, AyaNova.Api.ControllerHelpers.ApiServerState apiServerState, IServiceProvider serviceProvider)
{
_log.LogDebug("BOOT: configuring request pipeline...");
@@ -478,9 +521,9 @@ namespace AyaNova
}
}
- static Info CreateInfoForApiVersion(ApiVersionDescription description)
+ static Microsoft.OpenApi.Models.OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
{
- var info = new Info()
+ var info = new Microsoft.OpenApi.Models.OpenApiInfo()
{
Title = $"AyaNova API {description.ApiVersion}",
Version = description.ApiVersion.ToString()
diff --git a/server/AyaNova/SwaggerDefaultValues.cs b/server/AyaNova/SwaggerDefaultValues.cs
index 14d01e0d..eff78291 100644
--- a/server/AyaNova/SwaggerDefaultValues.cs
+++ b/server/AyaNova/SwaggerDefaultValues.cs
@@ -1,47 +1,47 @@
-namespace AyaNova
-{
- using Swashbuckle.AspNetCore.Swagger;
- using Swashbuckle.AspNetCore.SwaggerGen;
- using System.Linq;
+// namespace AyaNova
+// {
+// using Swashbuckle.AspNetCore.SwaggerGen;
+// using System.Linq;
- ///
- /// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter.
- ///
- /// This is only required due to bugs in the .
- /// Once they are fixed and published, this class can be removed.
- public class SwaggerDefaultValues : IOperationFilter
- {
- ///
- /// Applies the filter to the specified operation using the given context.
- ///
- /// The operation to apply the filter to.
- /// The current operation filter context.
- public void Apply( Operation operation, OperationFilterContext context )
- {
- // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412
- // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413
- foreach ( var parameter in operation.Parameters.OfType() )
- {
- var description = context.ApiDescription.ParameterDescriptions.First( p => p.Name == parameter.Name );
- var routeInfo = description.RouteInfo;
- if ( parameter.Description == null )
- {
- parameter.Description = description.ModelMetadata?.Description;
- }
+// ///
+// /// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter.
+// ///
+// /// This is only required due to bugs in the .
+// /// Once they are fixed and published, this class can be removed.
+// public class SwaggerDefaultValues : IOperationFilter
+// {
+// ///
+// /// Applies the filter to the specified operation using the given context.
+// ///
+// /// The operation to apply the filter to.
+// /// The current operation filter context.
+// public void Apply( Operation operation, OperationFilterContext context )
+// {
+// // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412
+// // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413
+// foreach ( var parameter in operation.Parameters.OfType() )
+// {
+// var description = context.ApiDescription.ParameterDescriptions.First( p => p.Name == parameter.Name );
+// var routeInfo = description.RouteInfo;
- if ( routeInfo == null )
- {
- continue;
- }
+// if ( parameter.Description == null )
+// {
+// parameter.Description = description.ModelMetadata?.Description;
+// }
- if ( parameter.Default == null )
- {
- parameter.Default = routeInfo.DefaultValue;
- }
+// if ( routeInfo == null )
+// {
+// continue;
+// }
- parameter.Required |= !routeInfo.IsOptional;
- }
- }
- }
-}
\ No newline at end of file
+// if ( parameter.Default == null )
+// {
+// parameter.Default = routeInfo.DefaultValue;
+// }
+
+// parameter.Required |= !routeInfo.IsOptional;
+// }
+// }
+// }
+// }
\ No newline at end of file
diff --git a/server/AyaNova/biz/EventLogProcessor.cs b/server/AyaNova/biz/EventLogProcessor.cs
index 07193827..71c9f9c6 100644
--- a/server/AyaNova/biz/EventLogProcessor.cs
+++ b/server/AyaNova/biz/EventLogProcessor.cs
@@ -40,7 +40,7 @@ namespace AyaNova.Biz
///
internal static void DeleteObject(long userId, AyaType ayType, long ayId, string textra, AyContext ct)
{
- ct.Database.ExecuteSqlCommand($"delete from aevent where aytype = {ayType} and ayid={ayId}");
+ ct.Database.ExecuteSqlInterpolated($"delete from aevent where aytype = {ayType} and ayid={ayId}");
ct.Event.Add(new Event(userId, ayId, ayType, AyaEvent.Deleted, textra));
}
diff --git a/server/AyaNova/biz/JobsBiz.cs b/server/AyaNova/biz/JobsBiz.cs
index d0a8b3c2..69f7bb43 100644
--- a/server/AyaNova/biz/JobsBiz.cs
+++ b/server/AyaNova/biz/JobsBiz.cs
@@ -217,11 +217,17 @@ namespace AyaNova.Biz
///
private static async Task removeJobAndLogsAsync(AyContext ct, Guid jobIdToBeDeleted)
{
- //delete logs
- await ct.Database.ExecuteSqlCommandAsync("delete from aopsjoblog where jobid = {0}", new object[] { jobIdToBeDeleted });
+ // //delete logs
+ // await ct.Database.ExecuteSqlCommandAsync("delete from aopsjoblog where jobid = {0}", new object[] { jobIdToBeDeleted });
+
+ // //delete the job
+ // await ct.Database.ExecuteSqlCommandAsync("delete from aopsjob where gid = {0}", new object[] { jobIdToBeDeleted });
+
+ //delete logs
+ await ct.Database.ExecuteSqlInterpolatedAsync($"delete from aopsjoblog where jobid = {jobIdToBeDeleted}");
//delete the job
- await ct.Database.ExecuteSqlCommandAsync("delete from aopsjob where gid = {0}", new object[] { jobIdToBeDeleted });
+ await ct.Database.ExecuteSqlInterpolatedAsync($"delete from aopsjob where gid = {jobIdToBeDeleted}");
}
diff --git a/server/AyaNova/biz/Search.cs b/server/AyaNova/biz/Search.cs
index bea0e1d3..e5a2f6e2 100644
--- a/server/AyaNova/biz/Search.cs
+++ b/server/AyaNova/biz/Search.cs
@@ -452,7 +452,7 @@ namespace AyaNova.Biz
//Be careful in future, if you put ToString at the end of each object in the string interpolation
//npgsql driver will assume it's a string and put quotes around it triggering an error that a string can't be compared to an int
AyContext ct = ServiceProviderProvider.DBContext;
- ct.Database.ExecuteSqlCommand($"delete from asearchkey where objectid={objectID} and objecttype={(int)objectType}");
+ ct.Database.ExecuteSqlInterpolated($"delete from asearchkey where objectid={objectID} and objecttype={(int)objectType}");
}
diff --git a/server/AyaNova/biz/UserBiz.cs b/server/AyaNova/biz/UserBiz.cs
index f816cc8f..99734c33 100644
--- a/server/AyaNova/biz/UserBiz.cs
+++ b/server/AyaNova/biz/UserBiz.cs
@@ -205,18 +205,29 @@ namespace AyaNova.Biz
#pragma warning disable EF1000
+ // var items = await ct.User
+ // .AsNoTracking()
+ // .FromSql(q)
+ // .Skip(pagingOptions.Offset.Value)
+ // .Take(pagingOptions.Limit.Value)
+ // .ToArrayAsync();
+
var items = await ct.User
- .AsNoTracking()
- .FromSql(q)
- .Skip(pagingOptions.Offset.Value)
- .Take(pagingOptions.Limit.Value)
- .ToArrayAsync();
+ .FromSqlRaw(q)
+ .AsNoTracking()
+ .Skip(pagingOptions.Offset.Value)
+ .Take(pagingOptions.Limit.Value)
+ .ToArrayAsync();
- var totalRecordCount = await ct.User
- .AsNoTracking()
- .FromSql(q)
- .CountAsync();
+ // var totalRecordCount = await ct.User
+ // .AsNoTracking()
+ // .FromSql(q)
+ // .CountAsync();
+
+ var totalRecordCount = await ct.User.FromSqlRaw(q)
+ .AsNoTracking()
+ .CountAsync();
#pragma warning restore EF1000
int itemCount = items.Count();//totalRecordCount doesn't skip and take so not usable here
@@ -364,7 +375,7 @@ namespace AyaNova.Biz
//Delete sibling objects
//USEROPTIONS
- ct.Database.ExecuteSqlCommand($"delete from auseroptions where userid={dbObj.Id}");
+ ct.Database.ExecuteSqlInterpolated($"delete from auseroptions where userid={dbObj.Id}");
EventLogProcessor.DeleteObject(UserId, BizType, dbObj.Id, dbObj.Name, ct);
diff --git a/server/AyaNova/biz/WidgetBiz.cs b/server/AyaNova/biz/WidgetBiz.cs
index a0eaedd7..fda38658 100644
--- a/server/AyaNova/biz/WidgetBiz.cs
+++ b/server/AyaNova/biz/WidgetBiz.cs
@@ -90,7 +90,7 @@ namespace AyaNova.Biz
outObj.Serial = ServerBootConfig.WIDGET_SERIAL.GetNext();
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
- outObj.CustomFields=JsonUtil.CompactJson(outObj.CustomFields);
+ outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
await ct.Widget.AddAsync(outObj);
await ct.SaveChangesAsync();
@@ -118,7 +118,7 @@ namespace AyaNova.Biz
//Test get serial id visible id number from generator
outObj.Serial = ServerBootConfig.WIDGET_SERIAL.GetNext();
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
- outObj.CustomFields=JsonUtil.CompactJson(outObj.CustomFields);
+ outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
TempContext.Widget.Add(outObj);
TempContext.SaveChanges();
@@ -142,9 +142,9 @@ namespace AyaNova.Biz
Widget outObj = new Widget();
CopyObject.Copy(dbObj, outObj);
- outObj.Name = Util.StringUtil.NameUniquify(outObj.Name,255);
- outObj.Id=0;
- outObj.ConcurrencyToken=0;
+ outObj.Name = Util.StringUtil.NameUniquify(outObj.Name, 255);
+ outObj.Id = 0;
+ outObj.ConcurrencyToken = 0;
//Test get serial id visible id number from generator
outObj.Serial = ServerBootConfig.WIDGET_SERIAL.GetNext();
@@ -176,7 +176,7 @@ namespace AyaNova.Biz
CopyObject.Copy(inObj, dbObj, "Id,Serial");
dbObj.Tags = TagUtil.NormalizeTags(dbObj.Tags);
- dbObj.CustomFields=JsonUtil.CompactJson(dbObj.CustomFields);
+ dbObj.CustomFields = JsonUtil.CompactJson(dbObj.CustomFields);
//Set "original" value of concurrency token to input token
//this will allow EF to check it out
@@ -210,7 +210,7 @@ namespace AyaNova.Biz
objectPatch.ApplyTo(dbObj);
dbObj.Tags = TagUtil.NormalizeTags(dbObj.Tags);
- dbObj.CustomFields=JsonUtil.CompactJson(dbObj.CustomFields);
+ dbObj.CustomFields = JsonUtil.CompactJson(dbObj.CustomFields);
ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = concurrencyToken;
Validate(dbObj, SnapshotOfOriginalDBObj);
@@ -233,7 +233,7 @@ namespace AyaNova.Biz
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserLocaleId, obj.Id, BizType, obj.Name);
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Serial).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
-
+
if (isNew)
Search.ProcessNewObjectKeywords(SearchParams);
else
@@ -322,16 +322,16 @@ namespace AyaNova.Biz
#pragma warning disable EF1000
var items = await ct.Widget
+ .FromSqlRaw(q)
.AsNoTracking()
- .FromSql(q)
.Skip(pagingOptions.Offset.Value)
.Take(pagingOptions.Limit.Value)
.ToArrayAsync();
var totalRecordCount = await ct.Widget
- .AsNoTracking()
- .FromSql(q)
- .CountAsync();
+ .FromSqlRaw(q)
+ .AsNoTracking()
+ .CountAsync();
#pragma warning restore EF1000
var pageLinks = new PaginationLinkBuilder(Url, routeName, null, pagingOptions, totalRecordCount).PagingLinksObject();
diff --git a/server/AyaNova/models/AyContext.cs b/server/AyaNova/models/AyContext.cs
index 5c50ab1b..3a9532bc 100644
--- a/server/AyaNova/models/AyContext.cs
+++ b/server/AyaNova/models/AyContext.cs
@@ -39,7 +39,8 @@ namespace AyaNova.Models
foreach (var entity in modelBuilder.Model.GetEntityTypes())
{
// Replace table names
- entity.Relational().TableName = "a" + entity.Relational().TableName.ToLowerInvariant();
+ //entity.Relational().TableName = "a" + entity.Relational().TableName.ToLowerInvariant();
+ entity.SetTableName( "a" + entity.GetTableName().ToLowerInvariant());
// Replace column names
foreach (var property in entity.GetProperties())
@@ -48,28 +49,34 @@ namespace AyaNova.Models
//set it up to work properly with PostgreSQL
if (property.Name == "ConcurrencyToken")
{
- property.Relational().ColumnName = "xmin";
- property.Relational().ColumnType = "xid";
- property.ValueGenerated = ValueGenerated.OnAddOrUpdate;
+ property.SetColumnName("xmin");
+ property.SetColumnType("xid");
+ // property.Relational().ColumnName = "xmin";
+ // property.Relational().ColumnType = "xid";
+ property.ValueGenerated = ValueGenerated.OnAddOrUpdate;
property.IsConcurrencyToken = true;
}
else
- property.Relational().ColumnName = property.Name.ToLowerInvariant();
+ property.SetColumnName(property.Name.ToLowerInvariant());
}
foreach (var key in entity.GetKeys())
{
- key.Relational().Name = key.Relational().Name.ToLowerInvariant();
+ key.SetName(key.GetName().ToLowerInvariant());
+ // key.Relational().Name = key.Relational().Name.ToLowerInvariant();
}
foreach (var key in entity.GetForeignKeys())
{
- key.Relational().Name = key.Relational().Name.ToLowerInvariant();
+ //key.Relational().Name = key.Relational().Name.ToLowerInvariant();
+
+ key.SetConstraintName(key.GetConstraintName().ToLowerInvariant());
}
foreach (var index in entity.GetIndexes())
{
- index.Relational().Name = index.Relational().Name.ToLowerInvariant();
+ index.SetName(index.GetName().ToLowerInvariant());
+ //index.Relational().Name = index.Relational().Name.ToLowerInvariant();
}
}