Just updated all the things and had to fix a bunch of stuff to get rid of the compiler errors, haven't run anything yet, just got it to compile at this point.

This commit is contained in:
2019-10-15 23:31:17 +00:00
parent a431ca32f6
commit aa177f4d48
10 changed files with 176 additions and 97 deletions

View File

@@ -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.

View File

@@ -31,13 +31,23 @@
<PackageReference Include="jose-jwt" Version="2.4.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.0-preview1.19508.20" />
<PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="3.1.0-preview1.19508.20" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.0-preview1.19508.20" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="4.0.0-preview8.19405.7" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="4.0.0-preview8.19405.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0-preview1.19506.2" />
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="3.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.OpenApi" Version="1.1.4" />
<PackageReference Include="NLog" Version="4.6.7" />
<PackageReference Include="NLog.Web.AspNetCore" Version="4.9.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc4" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="5.0.0-rc4" />
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="5.0.0-rc4" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.0.0-rc4" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUi" Version="5.0.0-rc4" />
</ItemGroup>
<Target Name="CopyCustomContent" AfterTargets="AfterBuild">

View File

@@ -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<Startup> logger, ILoggerFactory logFactory, Microsoft.AspNetCore.Hosting.IHostingEnvironment hostingEnvironment)
public Startup(ILogger<Startup> logger, ILoggerFactory logFactory, Microsoft.AspNetCore.Hosting.IWebHostEnvironment hostingEnvironment)
{
_log = logger;
_hostingEnvironment = hostingEnvironment;
@@ -48,7 +53,7 @@ namespace AyaNova
private readonly ILogger<Startup> _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<SwaggerDefaultValues>();
//Removed because will no longer compile the SwaggerDefaultValues but may be needed, not sure it's all a bit muddled
// c.OperationFilter<SwaggerDefaultValues>();
// 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<string, System.Collections.Generic.IEnumerable<string>>
//Obsolete way
// c.AddSecurityRequirement(new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>
// {
// { "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<string>()
}
});
});
@@ -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()

View File

@@ -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;
/// <summary>
/// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter.
/// </summary>
/// <remarks>This <see cref="IOperationFilter"/> is only required due to bugs in the <see cref="SwaggerGenerator"/>.
/// Once they are fixed and published, this class can be removed.</remarks>
public class SwaggerDefaultValues : IOperationFilter
{
/// <summary>
/// Applies the filter to the specified operation using the given context.
/// </summary>
/// <param name="operation">The operation to apply the filter to.</param>
/// <param name="context">The current operation filter context.</param>
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<NonBodyParameter>() )
{
var description = context.ApiDescription.ParameterDescriptions.First( p => p.Name == parameter.Name );
var routeInfo = description.RouteInfo;
if ( parameter.Description == null )
{
parameter.Description = description.ModelMetadata?.Description;
}
// /// <summary>
// /// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter.
// /// </summary>
// /// <remarks>This <see cref="IOperationFilter"/> is only required due to bugs in the <see cref="SwaggerGenerator"/>.
// /// Once they are fixed and published, this class can be removed.</remarks>
// public class SwaggerDefaultValues : IOperationFilter
// {
// /// <summary>
// /// Applies the filter to the specified operation using the given context.
// /// </summary>
// /// <param name="operation">The operation to apply the filter to.</param>
// /// <param name="context">The current operation filter context.</param>
// 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<NonBodyParameter>() )
// {
// 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;
}
}
}
}
// if ( parameter.Default == null )
// {
// parameter.Default = routeInfo.DefaultValue;
// }
// parameter.Required |= !routeInfo.IsOptional;
// }
// }
// }
// }

View File

@@ -40,7 +40,7 @@ namespace AyaNova.Biz
/// <param name="ct"></param>
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));
}

View File

@@ -217,11 +217,17 @@ namespace AyaNova.Biz
/// <param name="jobIdToBeDeleted"></param>
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}");
}

View File

@@ -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}");
}

View File

@@ -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);

View File

@@ -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();

View File

@@ -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();
}
}