From 74e03db0ee46055b74cff02180e0d619cab9a1f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Poul=20Kjeldager=20S=C3=B8rensen?= Date: Wed, 18 Oct 2023 10:04:44 +0200 Subject: [PATCH] fix: moved the SDK to this repository to avoid circulair references when using submodules --- EAVFramework.sln | 7 + sdk/DefaultManifestPathExtracter.cs | 34 + sdk/DefaultManifestReplacementRunner.cs | 295 ++++++ sdk/DefaultSchemaNameManager.cs | 10 + sdk/EAVFW.Extensions.Manifest.SDK.csproj | 30 + sdk/IManifestEnricher.cs | 12 + sdk/IManifestPathExtracter.cs | 9 + sdk/IManifestReplacmentRunner.cs | 11 + sdk/ISchemaNameManager.cs | 7 + sdk/ManifestEnricher.cs | 879 ++++++++++++++++++ sdk/ManifestEnricherOptions.cs | 9 + sdk/README.md | 2 + sdk/ServiceRegistrationExtension.cs | 20 + src/EAVFramework.csproj | 9 +- .../EAVFramework.UnitTest.csproj | 3 +- 15 files changed, 1330 insertions(+), 7 deletions(-) create mode 100644 sdk/DefaultManifestPathExtracter.cs create mode 100644 sdk/DefaultManifestReplacementRunner.cs create mode 100644 sdk/DefaultSchemaNameManager.cs create mode 100644 sdk/EAVFW.Extensions.Manifest.SDK.csproj create mode 100644 sdk/IManifestEnricher.cs create mode 100644 sdk/IManifestPathExtracter.cs create mode 100644 sdk/IManifestReplacmentRunner.cs create mode 100644 sdk/ISchemaNameManager.cs create mode 100644 sdk/ManifestEnricher.cs create mode 100644 sdk/ManifestEnricherOptions.cs create mode 100644 sdk/README.md create mode 100644 sdk/ServiceRegistrationExtension.cs diff --git a/EAVFramework.sln b/EAVFramework.sln index ef67afe..902d80e 100644 --- a/EAVFramework.sln +++ b/EAVFramework.sln @@ -28,6 +28,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{ .github\workflows\todo.yml = .github\workflows\todo.yml EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EAVFW.Extensions.Manifest.SDK", "sdk\EAVFW.Extensions.Manifest.SDK.csproj", "{B67A9BF3-506D-4AE1-B08A-F92564EF8B0D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -46,6 +48,10 @@ Global {2D94A574-9FE3-4DAF-B9EA-F9B57F0F358A}.Debug|Any CPU.Build.0 = Debug|Any CPU {2D94A574-9FE3-4DAF-B9EA-F9B57F0F358A}.Release|Any CPU.ActiveCfg = Release|Any CPU {2D94A574-9FE3-4DAF-B9EA-F9B57F0F358A}.Release|Any CPU.Build.0 = Release|Any CPU + {B67A9BF3-506D-4AE1-B08A-F92564EF8B0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B67A9BF3-506D-4AE1-B08A-F92564EF8B0D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B67A9BF3-506D-4AE1-B08A-F92564EF8B0D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B67A9BF3-506D-4AE1-B08A-F92564EF8B0D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -54,6 +60,7 @@ Global {13EA7011-EAAD-4711-9C56-335E3AA6A460} = {000AD218-EE1D-4478-A4FB-BEC306DCFBD1} {6559459D-C82B-4D31-B1F9-53C1B40391BB} = {000AD218-EE1D-4478-A4FB-BEC306DCFBD1} {D6322B62-74C6-4F70-9654-95DEEC0BEE00} = {E09CFDBE-BEF9-4A00-BC75-EAFE614F357B} + {B67A9BF3-506D-4AE1-B08A-F92564EF8B0D} = {000AD218-EE1D-4478-A4FB-BEC306DCFBD1} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {39A465A1-3369-4D33-A6F3-32C4FA834213} diff --git a/sdk/DefaultManifestPathExtracter.cs b/sdk/DefaultManifestPathExtracter.cs new file mode 100644 index 0000000..a21b377 --- /dev/null +++ b/sdk/DefaultManifestPathExtracter.cs @@ -0,0 +1,34 @@ +using Newtonsoft.Json.Linq; +using System.Linq; + +namespace EAVFW.Extensions.Manifest.SDK +{ + public class DefaultManifestPathExtracter : IManifestPathExtracter + { + public string ExtractPath(JToken token, string part) + { + string partPath; + if (token.Path.Contains(part) && !token.Path.EndsWith(part)) + { + + var idx = token.Path.IndexOf(part) + part.Length + 1; + + partPath = new string(token.Path.TakeWhile((c, i) => i < idx || !(c == '.' || c == ']')).ToArray()); + + if (partPath.EndsWith('\'')) + partPath += ']'; + + } + else + { + partPath = string.Empty; + + } + if (partPath.EndsWith("[merge()]")) + { + partPath = partPath.Replace("[merge()]", ""); + } + return string.IsNullOrEmpty(partPath) ? null : partPath; + } + } +} \ No newline at end of file diff --git a/sdk/DefaultManifestReplacementRunner.cs b/sdk/DefaultManifestReplacementRunner.cs new file mode 100644 index 0000000..2df0862 --- /dev/null +++ b/sdk/DefaultManifestReplacementRunner.cs @@ -0,0 +1,295 @@ +using DotNETDevOps.JsonFunctions; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace EAVFW.Extensions.Manifest.SDK +{ + public class DefaultManifestReplacementRunner : IManifestReplacmentRunner + { + private readonly ManifestEnricherOptions options; + private readonly ISchemaNameManager schemaNameManager; + private readonly IManifestPathExtracter manifestPathExtracter; + + public DefaultManifestReplacementRunner(IOptions options,ISchemaNameManager schemaNameManager, IManifestPathExtracter manifestPathExtracter) + { + this.options = options?.Value ?? throw new ArgumentNullException(nameof(options)); + this.schemaNameManager = schemaNameManager ?? throw new ArgumentNullException(nameof(schemaNameManager)); + this.manifestPathExtracter = manifestPathExtracter ?? throw new ArgumentNullException(nameof(manifestPathExtracter)); + } + + public virtual bool ShouldEvaluate(string str) + { + return str.StartsWith("[") && str.EndsWith("]") && !str.StartsWith("[["); + } + + public virtual async Task EvaluateAsync(ExpressionParser expressionParser, string str) + { + try + { + var nToken = await expressionParser.EvaluateAsync(str); + + if (nToken == null) + { + return nToken; + } + + + + if (nToken.Type == JTokenType.Object) + { + var q = new Queue(); + q.Enqueue(nToken); + while (q.Count > 0) + { + var c = q.Dequeue(); + if (c is JObject o) + { + foreach (var p in o.Properties()) + q.Enqueue(p); + + } + else if (c is JProperty p) + { + if (p.Name.StartsWith("[[")) + { + var nprop = new JProperty(p.Name.Substring(1, p.Name.Length - 2), p.Value); + p.Replace(nprop); + q.Enqueue(nprop); + } + else + { + q.Enqueue(p.Value); + } + } + else if (c is JArray a) + { + foreach (var e in a) + q.Enqueue(e); + } + else if (c.Type == JTokenType.String && c.ToString().StartsWith("[[")) + { + // var ch = await expressionParser.EvaluateAsync(c.ToString().Substring(1, c.ToString().Length - 2)); + // c.Replace(ch); + // q.Enqueue(ch); + var child = c.ToString().Substring(1, c.ToString().Length - 2); + // var childToken = await EvaluateAsync(expressionParser, child); + c.Replace(child); + } + } + } + + + + while (nToken.Type == JTokenType.String && ShouldEvaluate(nToken.ToString().Substring(1, nToken.ToString().Length - 2))) + { + nToken = await expressionParser.EvaluateAsync(nToken.ToString().Substring(1, nToken.ToString().Length - 2)); + } + + + + return nToken; + } + catch (Exception ex) + { + Console.WriteLine("EvaluateAsync: " + ex.ToString()); + throw; + } + } + + + + public async Task RunReplacements(JToken jsonraw, string customizationprefix, ILogger logger, JToken elementToRunReplacementFor = null) + { + var entityPath = string.Empty; + var attributePath = string.Empty; + JToken currentElement = null; + JToken localelement = null; + JToken[] localarguments = null; + + var q = new Queue(new[] { elementToRunReplacementFor ?? jsonraw }); + + + var expressionParser = new ExpressionParser( + Options.Create(new ExpressionParserOptions() { Document = jsonraw, ThrowOnError = true }), logger, + new DefaultExpressionFunctionFactory() + { + Functions = + { + ["data"] = (parser,Document,arguments) => {Console.WriteLine(arguments[0]); var child=JToken.Parse(File.ReadAllText(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(options.Path) ,arguments[0]?.ToString())));// q.Enqueue(child); + return Task.FromResult(child); }, + ["customizationprefix"] =(parser,Document,arguments) => Task.FromResult(customizationprefix), + ["propertyNames"] = (parser,document,arguments) => Task.FromResult((arguments[0] is JObject obj ? JToken.FromObject( obj.Properties().Select(k=>k.Name)):new JArray())), + ["indexOf"] =(parser,document,arguments) => Task.FromResult(Array.IndexOf( arguments[0].ToArray(),arguments[1])), + ["default"] = (parser,document,arguments) => Task.FromResult(arguments[0] == null || arguments[0].Type == JTokenType.Null ? arguments[1]:arguments[0]), + ["unfoldsource"] = (parser,document,arguments)=> Task.FromResult(document.SelectToken(arguments[0]["__unroll__path"].ToString())), + ["if"] = (parser,document,arguments) => Task.FromResult(arguments[0].ToObject() ? arguments[1]:arguments[2]), + ["condition"] = (parser,document,arguments) => Task.FromResult(arguments[0].ToObject() ? arguments[1]:arguments[2]), + ["in"] =(parser,document,arguments) => Task.FromResult(JToken.FromObject( arguments[1] != null && ( arguments[1] is JObject obj ? obj.ContainsKey(arguments[0].ToString()) : arguments[1].Any(a=>arguments[0].Equals(a))) )), + ["variables"] = (parser,document,arguments)=> { localarguments= arguments; return Task.FromResult(jsonraw.SelectToken($"$.variables.{arguments.First()}")?.DeepClone()); }, + ["concat"] = (parser,document,arguments)=>Task.FromResult(string.Join("",arguments.Select(k=>k.ToString())) ), + ["entity"] = (parser, document, arguments) => + { + var entity = document.SelectToken(entityPath); + + return Task.FromResult(entity); + }, + ["toLogicalName"] = (parser,document,arguments) => Task.FromResult(schemaNameManager.ToSchemaName(arguments[0].ToString()).ToLower()), + ["attribute"] = (parser, document, arguments) => Task.FromResult(document.SelectToken(attributePath)), + ["attributes"] = (parser, document, arguments) => Task.FromResult(document.SelectToken(entityPath+".attributes")), + ["select"] = (parser, document, arguments) => Task.FromResult(arguments.FirstOrDefault(a=>!(a== null || a.Type == JTokenType.Null))), + ["propertyName"] = (parser, document, arguments) => Task.FromResult( arguments[0].Parent is JProperty prop ? prop.Name : null), + ["parent"] =(parser, document, arguments) => Task.FromResult( arguments.Any() ? (arguments[0].Parent is JProperty prop ? prop.Parent:arguments[0].Parent) : (currentElement.Parent is JProperty prop1 ? prop1.Parent:currentElement.Parent)), + ["element"]=(parser,document,arguments)=>Task.FromResult(localelement ?? currentElement), + ["map"] =async (parser, document, arguments) =>{ + + return JToken.FromObject( await Task.WhenAll( arguments[0].Select(a=>{ + + localelement = a; + + return parser.EvaluateAsync(arguments[1].ToString()); + + + }))); + + } + + + } + }); + + while (q.Count > 0) + { + + var a = q.Dequeue(); + if (a == null) + continue; + + entityPath = manifestPathExtracter.ExtractPath(a, "entities"); + attributePath = manifestPathExtracter.ExtractPath(a, "attributes") ?? manifestPathExtracter.ExtractPath(a, "columns"); + + try + { + if (a is JProperty prop) + { + var value = prop.Value; + var str = prop.Name; + + if (ShouldEvaluate(str)) + { + + if (str == "[merge()]") + { + var parentObj = prop.Parent as JObject; + var obj = prop.Value; + + if (obj.Type == JTokenType.String && ShouldEvaluate(obj.ToString())) + { + currentElement = obj; + obj = await EvaluateAsync(expressionParser, obj.ToString()); + } + + foreach (var childProp in (obj as JObject).Properties().ToArray()) + { + + childProp.Remove(); + + if (parentObj.ContainsKey(childProp.Name) && childProp.Value is JObject source && parentObj[childProp.Name] is JObject target) + { + target.Merge(source); + q.Enqueue(parentObj.Property(childProp.Name)); + // parentObj[childProp.Name] + } + else if (parentObj.ContainsKey(childProp.Name) && JToken.DeepEquals(childProp.Value, parentObj[childProp.Name])) + { + q.Enqueue(parentObj.Property(childProp.Name)); + } + else + { + try + { + parentObj.Add(childProp); + q.Enqueue(childProp); + }catch(Exception ex) + { + throw; + } + } + + // parentObj.Add(childProp.Name, childProp.Value); + + } + + prop.Remove(); + continue; + } + currentElement = prop.Value; + var nToken = await EvaluateAsync(expressionParser, str); + + + + if (nToken.Type == JTokenType.Null || nToken.Type == JTokenType.Undefined) + { + prop.Remove(); + continue; + } + + + + var nProp = new JProperty(nToken.ToString(), value); + prop.Replace(nProp); + q.Enqueue(nProp); + } + else + { + + + q.Enqueue(value); + } + } + else if (a is JObject obj) + { + foreach (var p in obj.Properties()) + { + + q.Enqueue(p); + + + } + + } + else if (a is JArray array) + { + foreach (var element in array) + q.Enqueue(element); + + } + else if (a.Type == JTokenType.String) + { + var str = a.ToString(); + + if (ShouldEvaluate(str)) + { + currentElement = a; + var t = await EvaluateAsync(expressionParser, str); + + a.Replace(t); + q.Enqueue(t); + } + } + + } + catch (Exception ex) + { + Console.WriteLine($"{entityPath}| {attributePath}"); + throw; + } + } + } + } +} \ No newline at end of file diff --git a/sdk/DefaultSchemaNameManager.cs b/sdk/DefaultSchemaNameManager.cs new file mode 100644 index 0000000..b8ca7a2 --- /dev/null +++ b/sdk/DefaultSchemaNameManager.cs @@ -0,0 +1,10 @@ +namespace EAVFW.Extensions.Manifest.SDK +{ + public class DefaultSchemaNameManager : ISchemaNameManager + { + public string ToSchemaName(string displayName) + { + return displayName?.Replace(" ", "").Replace(":", "_").Replace("/", "or").Replace("-", "").Replace("(", "").Replace(")", "").Replace(".",""); ; + } + } +} \ No newline at end of file diff --git a/sdk/EAVFW.Extensions.Manifest.SDK.csproj b/sdk/EAVFW.Extensions.Manifest.SDK.csproj new file mode 100644 index 0000000..e07e215 --- /dev/null +++ b/sdk/EAVFW.Extensions.Manifest.SDK.csproj @@ -0,0 +1,30 @@ + + + + netcoreapp3.1;net6.0 + disable + disable + + EAVFW.Extensions.Manifest.SDK + Poul Kjeldager + SDK for EAVFW Manifest + README.md + https://github.com/EAVFW/EAVFW.Extensions.Manifest.ManifestEnricherTool + MIT + + + + + + + + + + + + + + + + + diff --git a/sdk/IManifestEnricher.cs b/sdk/IManifestEnricher.cs new file mode 100644 index 0000000..902efcc --- /dev/null +++ b/sdk/IManifestEnricher.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using System.Text.Json; +using System.Threading.Tasks; + +namespace EAVFW.Extensions.Manifest.SDK +{ + public interface IManifestEnricher + { + Task LoadJsonDocumentAsync(JToken jsonraw, string customizationprefix, ILogger logger); + } +} \ No newline at end of file diff --git a/sdk/IManifestPathExtracter.cs b/sdk/IManifestPathExtracter.cs new file mode 100644 index 0000000..1fc58af --- /dev/null +++ b/sdk/IManifestPathExtracter.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json.Linq; + +namespace EAVFW.Extensions.Manifest.SDK +{ + public interface IManifestPathExtracter + { + string ExtractPath(JToken token, string part); + } +} \ No newline at end of file diff --git a/sdk/IManifestReplacmentRunner.cs b/sdk/IManifestReplacmentRunner.cs new file mode 100644 index 0000000..4d9c3c6 --- /dev/null +++ b/sdk/IManifestReplacmentRunner.cs @@ -0,0 +1,11 @@ +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using System.Threading.Tasks; + +namespace EAVFW.Extensions.Manifest.SDK +{ + public interface IManifestReplacmentRunner + { + Task RunReplacements(JToken jsonraw, string customizationprefix, ILogger logger, JToken elementToRunReplacementFor = null); + } +} \ No newline at end of file diff --git a/sdk/ISchemaNameManager.cs b/sdk/ISchemaNameManager.cs new file mode 100644 index 0000000..e4e660f --- /dev/null +++ b/sdk/ISchemaNameManager.cs @@ -0,0 +1,7 @@ +namespace EAVFW.Extensions.Manifest.SDK +{ + public interface ISchemaNameManager + { + string ToSchemaName(string value); + } +} \ No newline at end of file diff --git a/sdk/ManifestEnricher.cs b/sdk/ManifestEnricher.cs new file mode 100644 index 0000000..a33b354 --- /dev/null +++ b/sdk/ManifestEnricher.cs @@ -0,0 +1,879 @@ +using DotNETDevOps.JsonFunctions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +namespace EAVFW.Extensions.Manifest.SDK +{ + + public interface IManifestPermissionGenerator + { + + Task CreateInitializationScript(JToken model, string systemUserEntity); + + } + public interface IParameterGenerator + { + string GetParameter(string name, bool escape=true); + } + public class SQLClientParameterGenerator : IParameterGenerator + { + public string GetParameter(string name, bool escape) + { + if(escape) + return $"'$({name})'"; + return $"$({name})"; + } + } + public class DataClientParameterGenerator : IParameterGenerator + { + public string GetParameter(string name, bool escape) + { + return $"@{name}"; + } + } + public class ManifestPermissionGenerator : IManifestPermissionGenerator + { + private readonly IParameterGenerator parameterGenerator; + + public ManifestPermissionGenerator(IParameterGenerator parameterGenerator) + { + this.parameterGenerator = parameterGenerator ?? throw new ArgumentNullException(nameof(parameterGenerator)); + } + public async Task CreateInitializationScript(JToken model, string systemUserEntity) + { + + var sb = new StringBuilder(); + var adminSGId = parameterGenerator.GetParameter("SystemAdminSecurityGroupId");// "$(SystemAdminSecurityGroupId)"; + + sb.AppendLine("DECLARE @adminSRId uniqueidentifier"); + sb.AppendLine("DECLARE @permissionId uniqueidentifier"); + sb.AppendLine($"SET @adminSRId = ISNULL((SELECT s.Id FROM [{parameterGenerator.GetParameter("DBName",false)}].[{parameterGenerator.GetParameter("DBSchema",false)}].[SecurityRoles] s WHERE s.Name = 'System Administrator'),'{Guid.NewGuid()}')"); + sb.AppendLine($"IF NOT EXISTS(SELECT * FROM [{parameterGenerator.GetParameter("DBName",false)}].[{parameterGenerator.GetParameter("DBSchema",false)}].[Identities] WHERE [Id] = {adminSGId})"); + sb.AppendLine("BEGIN"); + sb.AppendLine($"INSERT INTO [{parameterGenerator.GetParameter("DBName",false)}].[{parameterGenerator.GetParameter("DBSchema",false)}].[Identities] (Id, Name, ModifiedOn,CreatedOn,CreatedById,ModifiedById,OwnerId) VALUES({adminSGId}, 'System Administrator Group', CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,{adminSGId},{adminSGId},{adminSGId})"); + sb.AppendLine($"INSERT INTO [{parameterGenerator.GetParameter("DBName",false)}].[{parameterGenerator.GetParameter("DBSchema",false)}].[SecurityGroups] (Id) VALUES({adminSGId})"); + sb.AppendLine($"INSERT INTO [{parameterGenerator.GetParameter("DBName",false)}].[{parameterGenerator.GetParameter("DBSchema",false)}].[Identities] (Id, Name,ModifiedOn,CreatedOn,CreatedById,ModifiedById,OwnerId) VALUES ({parameterGenerator.GetParameter("UserGuid")}, {parameterGenerator.GetParameter("UserName")}, CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,{adminSGId},{adminSGId},{adminSGId})"); + sb.AppendLine($"INSERT INTO [{parameterGenerator.GetParameter("DBName",false)}].[{parameterGenerator.GetParameter("DBSchema",false)}].[{systemUserEntity}] (Id,Email) VALUES ({parameterGenerator.GetParameter("UserGuid")}, {parameterGenerator.GetParameter("UserEmail")});"); + sb.AppendLine($"INSERT INTO [{parameterGenerator.GetParameter("DBName",false)}].[{parameterGenerator.GetParameter("DBSchema",false)}].[SecurityRoles] (Name, Description, Id,ModifiedOn,CreatedOn,CreatedById,ModifiedById,OwnerId) VALUES('System Administrator', 'Access to all permissions', @adminSRId, CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,{adminSGId},{adminSGId},{adminSGId})"); + sb.AppendLine($"INSERT INTO [{parameterGenerator.GetParameter("DBName",false)}].[{parameterGenerator.GetParameter("DBSchema",false)}].[SecurityRoleAssignments] (IdentityId, SecurityRoleId, Id,ModifiedOn,CreatedOn,CreatedById,ModifiedById,OwnerId) VALUES({adminSGId}, @adminSRId, '{Guid.NewGuid()}',CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,{adminSGId},{adminSGId},{adminSGId})"); + sb.AppendLine($"INSERT INTO [{parameterGenerator.GetParameter("DBName",false)}].[{parameterGenerator.GetParameter("DBSchema",false)}].[SecurityGroupMembers] (IdentityId, SecurityGroupId, Id,ModifiedOn,CreatedOn,CreatedById,ModifiedById,OwnerId) VALUES({parameterGenerator.GetParameter("UserGuid")}, {adminSGId}, '{Guid.NewGuid()}',CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,{adminSGId},{adminSGId},{adminSGId})"); + sb.AppendLine("END;"); + foreach (var entitiy in model.SelectToken("$.entities").OfType()) + { + WritePermissionStatement(sb, entitiy, "ReadGlobal", "Global Read", adminSGId,true); + WritePermissionStatement(sb, entitiy, "Read", "Read", adminSGId); + WritePermissionStatement(sb, entitiy, "UpdateGlobal", "Global Update", adminSGId, true); + WritePermissionStatement(sb, entitiy, "Update", "Update", adminSGId); + WritePermissionStatement(sb, entitiy, "CreateGlobal", "Global Create", adminSGId, true); + WritePermissionStatement(sb, entitiy, "Create", "Create", adminSGId); + WritePermissionStatement(sb, entitiy, "DeleteGlobal", "Global Delete", adminSGId, true); + WritePermissionStatement(sb, entitiy, "Delete", "Delete", adminSGId); + WritePermissionStatement(sb, entitiy, "ShareGlobal", "Global Share", adminSGId, true); + WritePermissionStatement(sb, entitiy, "Share", "Share", adminSGId); + WritePermissionStatement(sb, entitiy, "AssignGlobal", "Global Assign", adminSGId, true); + WritePermissionStatement(sb, entitiy, "Assign", "Assign", adminSGId); + } + + return sb.ToString(); + } + private void WritePermissionStatement(StringBuilder sb, JProperty entitiy, string permission, string permissionName, string adminSGId, bool adminSRId1 = false) + { + sb.AppendLine($"SET @permissionId = ISNULL((SELECT s.Id FROM [{parameterGenerator.GetParameter("DBName",false)}].[{parameterGenerator.GetParameter("DBSchema",false)}].[Permissions] s WHERE s.Name = '{entitiy.Value.SelectToken("$.collectionSchemaName")}{permission}'),'{Guid.NewGuid()}')"); + sb.AppendLine($"IF NOT EXISTS(SELECT * FROM [{parameterGenerator.GetParameter("DBName",false)}].[{parameterGenerator.GetParameter("DBSchema",false)}].[Permissions] WHERE [Name] = '{entitiy.Value.SelectToken("$.collectionSchemaName")}{permission}')"); + sb.AppendLine("BEGIN"); + sb.AppendLine($"INSERT INTO [{parameterGenerator.GetParameter("DBName",false)}].[{parameterGenerator.GetParameter("DBSchema",false)}].[Permissions] (Name, Description, Id, ModifiedOn,CreatedOn,CreatedById,ModifiedById,OwnerId) VALUES('{entitiy.Value.SelectToken("$.collectionSchemaName")}{permission}', '{permissionName} access to {entitiy.Value.SelectToken("$.pluralName")}', @permissionId, CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,{adminSGId},{adminSGId},{adminSGId})"); + sb.AppendLine("END"); + if (adminSRId1) + { + sb.AppendLine($"IF NOT EXISTS(SELECT * FROM [{parameterGenerator.GetParameter("DBName",false)}].[{parameterGenerator.GetParameter("DBSchema",false)}].[SecurityRolePermissions] WHERE [Name] = 'System Administrator - {entitiy.Value.SelectToken("$.collectionSchemaName")} - {permission}')"); + sb.AppendLine("BEGIN"); + sb.AppendLine($"INSERT INTO [{parameterGenerator.GetParameter("DBName",false)}].[{parameterGenerator.GetParameter("DBSchema",false)}].[SecurityRolePermissions] (Name, PermissionId, SecurityRoleId, Id,ModifiedOn,CreatedOn,CreatedById,ModifiedById,OwnerId) VALUES('System Administrator - {entitiy.Value.SelectToken("$.collectionSchemaName")} - {permission}', @permissionId, @adminSRId, '{Guid.NewGuid()}', CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,{adminSGId},{adminSGId},{adminSGId})"); + sb.AppendLine("END"); + } + } + } + public class ManifestEnricher : IManifestEnricher + { + private readonly ISchemaNameManager schemaName; + private readonly IManifestReplacmentRunner manifestReplacmentRunner; + + public ManifestEnricher(ISchemaNameManager schemaName, IManifestReplacmentRunner manifestReplacmentRunner) + { + this.schemaName = schemaName ?? throw new System.ArgumentNullException(nameof(schemaName)); + this.manifestReplacmentRunner = manifestReplacmentRunner ?? throw new ArgumentNullException(nameof(manifestReplacmentRunner)); + } + + protected virtual JObject Merge(JObject jToken, object obj) + { + + jToken = (JObject)jToken.DeepClone(); + + + var jobj = JToken.FromObject(obj) as JObject; + + foreach (var p in jobj.Properties()) + { + if (!(p.Value.Type == JTokenType.Null || p.Value.Type == JTokenType.Undefined)) + jToken[p.Name] = p.Value; + } + + if (!jToken.ContainsKey("schemaName")) + jToken["schemaName"] = schemaName.ToSchemaName(jToken.SelectToken("$.displayName")?.ToString()); + if (!jToken.ContainsKey("logicalName")) + jToken["logicalName"] = jToken.SelectToken("$.schemaName")?.ToString().ToLower(); + + return jToken as JObject; + } + private object[] CreateOptions(params string[] args) + { + return args.Select((o, i) => new { label = o, value = i + 1 }).ToArray(); + } + private JObject CreateAttribute(JObject attr, string displayName, object type, string? schemaName = null, object? additionalProps = null) + { + if (additionalProps != null) + return Merge(Merge(attr, new { displayName, type, schemaName }), additionalProps); + return Merge(attr, new { displayName, type, schemaName }); + } + private string TrimId(string v) + { + if (string.IsNullOrEmpty(v)) + return v; + + if (v.EndsWith("id", StringComparison.OrdinalIgnoreCase)) + return v.Substring(0, v.Length - 2); + + return v; + } + public async Task LoadJsonDocumentAsync(JToken jsonraw, string customizationprefix, ILogger logger) + { + + + var entities = jsonraw.SelectToken("$.entities") as JObject; + var insertMerges = jsonraw.SelectToken("$.variables.options.insertMergeLayoutVariable")?.ToObject(); + + + foreach (var entitieP in (entities)?.Properties() ?? Enumerable.Empty()) + { + var entity = (JObject)entitieP.Value; + + SetRequiredProps(entity, entitieP.Name); + + await EnrichEntity(jsonraw, customizationprefix, logger, insertMerges, entity); + + } + + + foreach (var entitieP in (entities)?.Properties().ToArray() ?? Enumerable.Empty()) + { + foreach (var polyLookup in entitieP.Value.SelectToken("$.attributes").OfType().Where(c => c.Value.SelectToken("$.type.type")?.ToString().ToLower() == "polylookup").ToArray()) + { + var name = polyLookup.Value.SelectToken("$.type.name")?.ToString() ?? $"{entitieP.Name} {polyLookup.Name}"; + var Key = name + " Reference"; + var pluralName = name + " References"; //$"{entitieP.Value.SelectToken("$.displayName")} {polyLookup.Value.SelectToken("$.displayName")} References"; + var reverse = polyLookup.Value.SelectToken("$.type.reverse")?.ToObject() ?? false; + var inline = polyLookup.Value.SelectToken("$.type.inline")?.ToObject() ?? false; + if (!entities.ContainsKey(Key)) + { + var attributes = polyLookup.Value.SelectToken("$.type.referenceTypes").ToObject() + .ToDictionary(k => k, v => JToken.FromObject(new { type = new { type = "lookup", referenceType = v } })); + + if (inline) + { + foreach (var attribute in attributes) + { + entitieP.Value["attributes"][attribute.Key] = attribute.Value; + + } + polyLookup.Value["type"]["type"] = "polylookup"; + // polyLookup.Remove(); + } + else + { + + + // attributes["Id"] = JToken.FromObject(new { isPrimaryKey = true }); + attributes["Name"] = JToken.FromObject(new { isPrimaryField = true }); + + + + entities[Key] = JToken.FromObject(new + { + pluralName = pluralName, + attributes = attributes + }); + + + SetRequiredProps(entities[Key] as JObject, Key); + } + } + + + + if (!inline) + { + var entity = entities[Key] as JObject; + polyLookup.Value["type"]["foreignKey"] = JToken.FromObject(new + { + principalTable = entity["logicalName"].ToString(), + principalColumn = "id", + principalNameColumn = "name", + name = TrimId(polyLookup.Value.SelectToken("$.logicalName")?.ToString()) // jsonraw.SelectToken($"$.entities['{ attr["type"]["referenceType"] }'].logicalName").ToString().Replace(" ", ""), + }); + polyLookup.Value["type"]["referenceType"] = Key; + + if (reverse) + { + entities[Key]["attributes"][polyLookup.Name] = JToken.FromObject(new + { + type = new + { + type = "lookup", + referenceType = entitieP.Name + } + }); + + // polyLookup.Remove(); + } + + + // polyLookup.Value["type"]["type"] = "lookup"; + + await EnrichEntity(jsonraw, customizationprefix, logger, insertMerges, entity); + } + else + { + + await EnrichEntity(jsonraw, customizationprefix, logger, insertMerges, entitieP.Value as JObject); + } + } + } + + + await manifestReplacmentRunner.RunReplacements(jsonraw, customizationprefix, logger); + + + foreach (var entitieP in (jsonraw.SelectToken("$.entities") as JObject)?.Properties() ?? Enumerable.Empty()) + { + var attributes = entitieP.Value.SelectToken("$.attributes") as JObject; + + foreach (var attributeDefinition in attributes.Properties()) + { + var attr = attributeDefinition.Value; + + switch (attr.SelectToken("$.type.type")?.ToString()?.ToLower()) + { + case "lookup" when string.IsNullOrEmpty(jsonraw.SelectToken($"$.entities['{attr["type"]["referenceType"]}'].logicalName")?.ToString()): + throw new KeyNotFoundException($"The lookup entity does not exists: '{attr["type"]["referenceType"]}'"); + case "lookup": + + + var columns = jsonraw.SelectToken($"$.entities['{attr["type"]["referenceType"]}'].attributes").OfType() + .Concat(jsonraw.SelectToken($"$.entities['{attr["type"]["referenceType"]}'].TPT") == null ? Enumerable.Empty() : jsonraw.SelectToken($"$.entities['{jsonraw.SelectToken($"$.entities['{attr["type"]["referenceType"]}'].TPT")}'].attributes").OfType()) + .GroupBy(k => k.Name).Select(g => g.First()) + .ToArray(); + + var principalTable = jsonraw.SelectToken($"$.entities['{attr["type"]["referenceType"]}'].logicalName").ToString(); + var principalColumn = columns + .FirstOrDefault(a => a.Value.SelectToken("$.isPrimaryKey")?.ToObject() ?? false) + ?.Value.SelectToken("$.logicalName").ToString() + ?? throw new InvalidOperationException($"Cant find principalColumn for lookup {entitieP.Name}.{attributeDefinition.Name}"); ; + + var principalNameColumn = columns + .FirstOrDefault(a => a.Value.SelectToken("$.isPrimaryField")?.ToObject() ?? false) + ?.Value.SelectToken("$.logicalName").ToString() + ?? throw new InvalidOperationException($"Cant find principalNameColumn for lookup {entitieP.Name}.{attributeDefinition.Name}"); + + attr["type"]["foreignKey"] = JToken.FromObject(new + { + principalTable = principalTable, + principalColumn = principalColumn, + principalNameColumn = principalNameColumn, + name = TrimId(attr.SelectToken("$.logicalName")?.ToString()) // jsonraw.SelectToken($"$.entities['{ attr["type"]["referenceType"] }'].logicalName").ToString().Replace(" ", ""), + }); + + + break; + case "float": + case "decimal": + if (attr.SelectToken("$.type.sql") == null) + { + attr["type"]["sql"] = JToken.FromObject(new { precision = 18, scale = 4 }); + } + if (attr.SelectToken("$.type.sql.precision") == null) + { + attr["type"]["sql"]["precision"] = 18; + } + if (attr.SelectToken("$.type.sql.scale") == null) + { + attr["type"]["sql"]["scale"] = 4; + } + break; + + } + + + } + } + + + + var defaultControls = jsonraw.SelectToken("$.controls"); + if (defaultControls != null) + { + logger.LogInformation("Replacing default Controls"); + + foreach (var defaultControl in defaultControls.OfType()) + { + logger.LogInformation("Replacing default Controls : {Type}", defaultControl.Name); + + foreach (var entity in jsonraw.SelectToken("$.entities")?.OfType() ?? Enumerable.Empty()) + { + foreach (var attribute in entity.Value.SelectToken("$.attributes")?.OfType() ?? Enumerable.Empty()) + { + var attributeType = (attribute.Value.SelectToken("$.type.type") ?? attribute.Value.SelectToken("$.type")).ToString(); + + if (string.Equals(attributeType, defaultControl.Name, StringComparison.OrdinalIgnoreCase)) + { + logger.LogInformation("Replacing default Controls for {entity} {attribute} : {type}", entity.Name, attribute.Name, defaultControl.Name); + + var formFields = (entity.Value.SelectToken($"$.forms")?.OfType() ?? Enumerable.Empty()) + .Select(c => c.Value.SelectToken($"$.columns['{attribute.Name}']")).Where(c => c != null).ToArray(); + + { + + foreach (var formField in formFields) + { + var control = formField.SelectToken("$.control"); + + if (control == null) + { + var replacement = defaultControl.Value.DeepClone(); ; + formField["control"] = replacement; + var q = new Queue(new[] { replacement }); + while (q.Any()) + { + var e = q.Dequeue(); + if (e is JObject obj) + { + foreach (var prop in e.OfType()) + { + q.Enqueue(prop); + } + } + else if (e is JProperty prop) + { + q.Enqueue(prop.Value); + } + else if (e is JArray array) + { + foreach (var ee in array) + { + q.Enqueue(ee); + } + } + else if (e.Type == JTokenType.String) + { + var str = e.ToString(); + if (str.StartsWith("[[") && str.EndsWith("]]")) + { + e.Replace(str.Substring(1, str.Length - 2)); + } + + } + } + + + logger.LogInformation("Replacing default Controls for {entity} {attribute} {formname}: {type}", entity.Name, attribute.Name, (formField.Parent.Parent.Parent as JProperty)?.Name, defaultControl.Name); + } + } + } + } + } + } + } + } + + await manifestReplacmentRunner.RunReplacements(jsonraw, customizationprefix, logger); + + + foreach (var (entityDefinition, attributeDefinition2) in jsonraw.SelectToken("$.entities").OfType() + .SelectMany(e => e.Value.SelectToken("$.attributes").OfType().Select(p => (e, p))) + .Where(a => a.p.Value.SelectToken("$.type.type")?.ToString().ToLower() == "choices") + .ToArray()) + { + + + + + var nentity = $"{attributeDefinition2.Value.SelectToken("$.type.name")}"; + + + jsonraw["entities"][nentity] = JToken.FromObject( + new + { + pluralName = $"{attributeDefinition2.Value.SelectToken("$.type.pluralName")}", + displayName = nentity, + logicalName = $"{attributeDefinition2.Value.SelectToken("$.type.name")}".Replace(" ", "").ToLower(), + schemaName = $"{attributeDefinition2.Value.SelectToken("$.type.name")}".Replace(" ", ""), + collectionSchemaName = $"{attributeDefinition2.Value.SelectToken("$.type.pluralName")}".Replace(" ", ""), + keys = new Dictionary + { + [$"IX_{entityDefinition.Name}Value"] = new[] { entityDefinition.Name, nentity + " Value" } + }, + attributes = new Dictionary + { + ["Id"] = new + { + displayName = "Id", + logicalName = "id", + schemaName = "Id", + type = new { type = "guid" }, + isPrimaryKey = true, + }, + [entityDefinition.Name] = new + { + displayName = entityDefinition.Value.SelectToken("$.displayName"), + logicalName = entityDefinition.Value.SelectToken("$.logicalName") + "id", + schemaName = entityDefinition.Value.SelectToken("$.schemaName") + "Id", + type = new + { + type = "lookup", + referenceType = entityDefinition.Name, + }, + }, + [nentity + " Value"] = new + { + + displayName = nentity + " Value", + logicalName = $"{attributeDefinition2.Value.SelectToken("$.type.name")}".Replace(" ", "").ToLower(), + schemaName = $"{attributeDefinition2.Value.SelectToken("$.type.name")}".Replace(" ", "") + "Value", + // isPrimaryKey = true, + type = new + { + type = "choice", + name = $"{attributeDefinition2.Value.SelectToken("$.type.name")}".Replace(" ", "") + "Value", + options = attributeDefinition2.Value.SelectToken("$.type.options") + } + } + } + }); + //attributeDefinition2.Value.SelectToken("$.type").Replace(JToken.FromObject( + // new + // { + // type = "lookup", + // referenceType = $"{attributeDefinition2.Value.SelectToken("$.type.name")}" + // } + // )); + + attributeDefinition2.Value["type"]["logicalName"] = $"{attributeDefinition2.Value.SelectToken("$.type.name")}".Replace(" ", "").ToLower(); + attributeDefinition2.Value["type"]["schemaName"] = $"{attributeDefinition2.Value.SelectToken("$.type.name")}".Replace(" ", ""); + attributeDefinition2.Value["type"]["collectionSchemaName"] = $"{attributeDefinition2.Value.SelectToken("$.type.pluralName")}".Replace(" ", ""); + attributeDefinition2.Value["type"]["principalColumn"] = entityDefinition.Value.SelectToken("$.logicalName") + "id"; + // attributeDefinition2.Remove(); + + } + + + //Lets sort them according to TPT + var qque = new Queue(jsonraw.SelectToken("$.entities").OfType()); + + while (qque.Count > 0) + { + var entity = qque.Dequeue(); + + var tpt = entity.Value.SelectToken("$.TPT")?.ToString(); + if (!string.IsNullOrEmpty(tpt)) + { + var baseentity = jsonraw.SelectToken($"$.entities['{tpt}']").Parent as JProperty; + entity.Remove(); + baseentity.AddAfterSelf(entity); + + + } + } + + + var json = JsonDocument.Parse(jsonraw.ToString(), new JsonDocumentOptions + { + CommentHandling = JsonCommentHandling.Skip + }); + Directory.CreateDirectory("obj"); + File.WriteAllText("obj/manifest.g.json", jsonraw.ToString(Newtonsoft.Json.Formatting.Indented)); + + + ///For loop over jsonraw.selectToken("$.entities") + ///write a file to obj/specs/.spec.g.json + ///containing a json schema file for the entity attributes. Sadly there is no strict type map of possible types. + ///Types can be anything random that i later maps to something in dynamics. (use tolower) + /// Currently from AttributeTypeCodeConverter - currency,customer,datetime,multilinetext,memo,int,integer,timezone,phone,float,guid,string,text,boolean,bool, + /// and type.type can be autonumber,choice,picklist,choices,state,status,lookup,string,text + + bool ConvertToSchemaType(JToken attrType, out JToken type) + { + type = null; + + var inp = attrType?.ToString(); + if (!(attrType.Type == JTokenType.String)) + { + inp = attrType.SelectToken("$.type")?.ToString(); + } + + switch (inp.ToLower()) + { + case "point": + type = JToken.FromObject(new + { + type = "object", + properties=new + { + + } + }); + return true; + case "binary": + type = JToken.FromObject(new + { + type = "string", + contentEncoding = "base64" + }); + return true; + case "datetime": + type = "datetime"; + return true; + case "time": + type = "time"; + return true; + + case "customer": + case "polylookup": + return false; + case "string": + case "text": + case "multilinetext": + type = "string"; + return true; + case "integer": + type = "integer"; + return true; + case "decimal": + type = "number"; + return true; + case "boolean": + type = "boolean"; + return true; + case "lookup": + + + var foreignTable = jsonraw.SelectToken($"$.entities['{attrType.SelectToken("$.referenceType")}']"); + var fatAttributes = foreignTable.SelectToken("$.attributes"); + var fat = fatAttributes.OfType().Where(c => c.Value.SelectToken("$.isPrimaryKey")?.ToObject() ?? false) + .Select(a => a.Value.SelectToken("$.type")).Single(); + if (fat.Type == JTokenType.Object) + fat = fat.SelectToken("$.type"); + + ConvertToSchemaType(fat?.ToString(), out type); + + type["x-foreign-key"] = JToken.FromObject(new + { + table = new + { + logicalName = foreignTable.SelectToken("$.logicalName"), + schemaName = foreignTable.SelectToken("$.schemaName"), + pluralName = foreignTable.SelectToken("$.pluralName"), + }, + columns = fatAttributes.OfType().Where(c => c.Value.SelectToken("$.isPrimaryKey")?.ToObject() ?? false) + .Select(a => new + { + logicalName = a.SelectToken("$.logicalName"), + schemaName = a.SelectToken("$.schemaName"), + + }) + }); + + return true; + + case "guid": + type = JToken.FromObject(new + { + type = "string", + format = "uuid" + }); + return true; + case "choices": + + type = JToken.FromObject(new + { + type = "array", + items = new + { + type = "integer", + @enum = attrType.SelectToken("$.options").OfType().Select(c => c.Value.ToObject()) + } + }); + return true; + case "choice": + type = JToken.FromObject(new + { + type = "integer", + @enum = attrType.SelectToken("$.options").OfType().Select(c => c.Value.Type == JTokenType.Object ? c.Value.SelectToken("$.value") : c.Value).Select(v => v.ToObject()) + }); + return true; + default: + throw new NotImplementedException(inp); + } + + + } + + Directory.CreateDirectory("obj/models"); + foreach (var entity in (jsonraw.SelectToken("$.entities") as JObject)?.Properties() ?? Enumerable.Empty()) + { + try + { + var entityValue = entity.Value as JObject; + var schema = new JObject + { + ["title"] = entity.Name, + ["$schema"] = "http://json-schema.org/draft-07/schema#", + ["type"] = "object", + }; + var properties = new JObject(); + + foreach (var attr in (entityValue.SelectToken("$.attributes") as JObject)?.Properties() ?? Enumerable.Empty()) + { + var attrValue = attr.Value as JObject; + var attrType = attrValue.SelectToken("$.type"); + + + + if (!ConvertToSchemaType(attrType, out var type)) continue; + + var propValues = new JObject(); + var logicalName = attrValue.SelectToken("$.logicalName").ToString(); + var displayName = attrValue.SelectToken("$.displayName").ToString(); + propValues["title"] = displayName; + propValues["type"] = type; + properties[logicalName] = propValues; + } + + schema["properties"] = properties; + + var filePath = $"obj/models/{entityValue["logicalName"]}.spec.g.json"; + File.WriteAllText(filePath, schema.ToString(Newtonsoft.Json.Formatting.Indented)); + } + catch (Exception ex) + { + Console.WriteLine($"Warning: Failed to generate jsonschema for {entity.Name}"); + Console.Write(ex); + } + } + + return json; + } + + private async Task EnrichEntity(JToken jsonraw, string customizationprefix, ILogger logger, string insertMerges, JObject entity) + { + JObject SetDefault(JToken obj, JObject localeEnglish) + { + var value = new JObject(new JProperty("1033", localeEnglish)); + obj["locale"] = value; + return value; + } + var entityLocaleEnglish = new JObject(new JProperty("displayName", entity["displayName"]), new JProperty("pluralName", entity["pluralName"])); + var entityLocale = entity.SelectToken("$.locale") as JObject ?? SetDefault(entity, entityLocaleEnglish); + if (!entityLocale.ContainsKey("1033")) + entityLocale["1033"] = entityLocaleEnglish; + + + var attributes = entity.SelectToken("$.attributes") as JObject; + + if (attributes == null) + { + entity["attributes"] = attributes = new JObject(); + } + + if (attributes != null) + { + if (!attributes.Properties().Any(p => p.Value.SelectToken("$.isPrimaryKey")?.ToObject() ?? false)) + { + attributes["Id"] = JToken.FromObject(new { isPrimaryKey = true, type = new { type = "guid" } }); + } + + + //Replace string attributes + foreach (var attr in attributes.Properties().ToArray()) + { + if (attr.Name == "[merge()]") + { + await manifestReplacmentRunner.RunReplacements(jsonraw, customizationprefix, logger, attr); + } + else if (attr.Value.Type == JTokenType.String) + { + await manifestReplacmentRunner.RunReplacements(jsonraw, customizationprefix, logger, attr.Value); + } + } + + var queue = new Queue(attributes.Properties().Select(c => c.Value as JObject)); + + foreach (var attribute in attributes.Properties()) + { + if (!string.IsNullOrEmpty(insertMerges)) + { + var value = attribute.Value as JObject; + if (!value?.ContainsKey("[merge()]") ?? false) + value.Add(new JProperty("[merge()]", $"[variables('{insertMerges}')]")); + queue.Enqueue(value); + } + } + + + while (queue.Count > 0) + { + var attr = queue.Dequeue(); + + + + if (!attr.ContainsKey("displayName")) + attr["displayName"] = (attr.Parent as JProperty)?.Name; + + if (attr["type"]?.ToString() == "address") + { + + var displayName = attr.SelectToken("$.displayName")?.ToString(); + + attr["__unroll__path"] = attr.Path; + + var unrolls = new[] { + Merge(attr,new { displayName=$"{displayName}: Address Type", type=new { type ="picklist", + isGlobal=false, + name=$"{displayName}: Address Type", + options=CreateOptions("Bill To","Ship To","Primary","Other") + } }), + Merge(attr,new { displayName=$"{displayName}: City", type="string"}), + Merge(attr,new { displayName=$"{displayName}: Country", type="string", schemaName=schemaName.ToSchemaName( $"{displayName}: Country")}), + Merge(attr,new { displayName=$"{displayName}: County", type="string"}), + Merge(attr,new { displayName=$"{displayName}: Fax", type="string"}), + Merge(attr,new { displayName=$"{displayName}: Freight Terms", schemaName=schemaName.ToSchemaName( $"{displayName}: Freight Terms Code"), type=new { type="picklist", + isGlobal=false, + name=$"{displayName}: Freight Terms", + options=CreateOptions("FOB","No Charge") + } }), + // Merge(attr,new { displayName=$"{displayName}: Id",schemaName=ToSchemaName( $"{displayName}: AddressId"),type ="guid"}), + CreateAttribute(attr,$"{displayName}: Latitude","float"), + CreateAttribute(attr,$"{displayName}: Longitude","float"), + CreateAttribute(attr,$"{displayName}: Name","string",null, new { isPrimaryField = !attributes.Properties().Any(p=>p.Value.SelectToken("$.isPrimaryField") != null) }), + CreateAttribute(attr,$"{displayName}: Phone","phone", schemaName.ToSchemaName( $"{displayName}: Telephone 1")), + CreateAttribute(attr,$"{displayName}: Telephone 2","phone", schemaName.ToSchemaName( $"{displayName}: Telephone 2")), + CreateAttribute(attr,$"{displayName}: Telephone 3","phone", schemaName.ToSchemaName( $"{displayName}: Telephone 3")), + CreateAttribute(attr,$"{displayName}: Post Office Box","string"), + CreateAttribute(attr,$"{displayName}: Primary Contact Name","string"), + CreateAttribute(attr,$"{displayName}: Shipping Method",new { type="picklist", + isGlobal=false, + name=$"{displayName}: Shipping Method", + options=CreateOptions("Airborne","DHL","FedEx","UPS","Postal Mail","Full Load","Will Call"), + }, schemaName.ToSchemaName( $"{displayName}: Shipping Method Code")), + CreateAttribute(attr,$"{displayName}: State/Province","string"), + CreateAttribute(attr,$"{displayName}: Street 1","string",schemaName.ToSchemaName( $"{displayName}: line1")), + CreateAttribute(attr,$"{displayName}: Street 2","string",schemaName.ToSchemaName( $"{displayName}: line2")), + CreateAttribute(attr,$"{displayName}: Street 3","string",schemaName.ToSchemaName( $"{displayName}: line3")), + CreateAttribute(attr,$"{displayName}: UPS Zone","string"), + CreateAttribute(attr,$"{displayName}: UTC Offset","timezone"), + CreateAttribute(attr,$"{displayName}: ZIP/Postal Code","string",schemaName.ToSchemaName( $"{displayName}: Postal Code")), + CreateAttribute(attr,$"{displayName}: State/Province","string"), + + }; + + attr["schemaName"] = displayName.Replace(" ", "").Replace(":", "_") + "_Composite"; + attr["type"] = "MultilineText"; + + + foreach (var unroll in unrolls) + { + queue.Enqueue(unroll); + } + + //if(!attributes.Properties().Any(p=>p.Value.SelectToken("$.isPrimaryField") != null)) + //{ + // attr["type"] = JObject.FromObject(new { type = "string", maxLength = 1024 }); + // attr["isPrimaryField"] = true; + //} + + } + + + if (!attr.ContainsKey("schemaName")) + { + + attr["schemaName"] = schemaName.ToSchemaName(attr.SelectToken("$.displayName").ToString()); + + await manifestReplacmentRunner.RunReplacements(jsonraw, customizationprefix, logger, attr); + + switch (attr.SelectToken("$.type.type")?.ToString()?.ToLower()) + { + case "lookup": + case "polylookup": + case "customer": + if (!attr["schemaName"].ToString().EndsWith("Id")) + attr["schemaName"] = $"{schemaName.ToSchemaName(attr.SelectToken("$.displayName").ToString())}Id"; + + + + break; + + } + } + + + if (!attr.ContainsKey("logicalName")) + attr["logicalName"] = attr.SelectToken("$.schemaName").ToString().ToLower(); + + if (!attr.ContainsKey("type")) + attr["type"] = "string"; + + if (attr.Parent == null && !(attributes.ContainsKey(attr["logicalName"].ToString()) || attributes.ContainsKey(attr["schemaName"].ToString()) || attributes.ContainsKey(attr["displayName"].ToString()))) + attributes[attr["logicalName"].ToString()] = attr; + + if (attr.SelectToken("$.type").Type == JTokenType.String) + { + attr["type"] = JToken.FromObject(new { type = attr.SelectToken("$.type") }); + } + + + + } + + foreach (var attr in attributes.Properties()) + { + var attributeLocaleEnglish = new JObject(new JProperty("displayName", attr.Value["displayName"])); + var attributeLocale = attr.Value.SelectToken("$.locale") as JObject ?? SetDefault(attr.Value, attributeLocaleEnglish); + if (!attributeLocale.ContainsKey("1033")) + attributeLocale["1033"] = attributeLocaleEnglish; + } + + + } + } + + private void SetRequiredProps(JObject entity, string key) + { + if (!entity.ContainsKey("displayName")) + entity["displayName"] = key; + if (!entity.ContainsKey("schemaName")) + entity["schemaName"] = entity.SelectToken("$.displayName")?.ToString().Replace(" ", ""); + if (!entity.ContainsKey("logicalName")) + entity["logicalName"] = entity.SelectToken("$.schemaName")?.ToString().ToLower(); + + if (!entity.ContainsKey("collectionSchemaName")) + entity["collectionSchemaName"] = schemaName.ToSchemaName(entity["pluralName"]?.ToString()); + } + } +} \ No newline at end of file diff --git a/sdk/ManifestEnricherOptions.cs b/sdk/ManifestEnricherOptions.cs new file mode 100644 index 0000000..9a8d76b --- /dev/null +++ b/sdk/ManifestEnricherOptions.cs @@ -0,0 +1,9 @@ +using System.IO; + +namespace EAVFW.Extensions.Manifest.SDK +{ + public class ManifestEnricherOptions + { + public string Path { get; set; } = Directory.GetCurrentDirectory(); + } +} \ No newline at end of file diff --git a/sdk/README.md b/sdk/README.md new file mode 100644 index 0000000..e2b898b --- /dev/null +++ b/sdk/README.md @@ -0,0 +1,2 @@ +# EAVFW Manifest SDK + diff --git a/sdk/ServiceRegistrationExtension.cs b/sdk/ServiceRegistrationExtension.cs new file mode 100644 index 0000000..179a005 --- /dev/null +++ b/sdk/ServiceRegistrationExtension.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace EAVFW.Extensions.Manifest.SDK +{ + public static class ServiceRegistrationExtension + { + public static IServiceCollection AddManifestSDK(this IServiceCollection services) where TParameterGenerator : class,IParameterGenerator + { + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddSingleton(); + services.AddOptions(); + + return services; + } + } +} \ No newline at end of file diff --git a/src/EAVFramework.csproj b/src/EAVFramework.csproj index dd1afc6..6772066 100644 --- a/src/EAVFramework.csproj +++ b/src/EAVFramework.csproj @@ -13,7 +13,7 @@ https://github.com/EAVFW/EAVFramework MIT - $(UseEAVFromNuget) + @@ -40,11 +40,8 @@ - - - - - + + diff --git a/test/EAVFramework.UnitTest/EAVFramework.UnitTest.csproj b/test/EAVFramework.UnitTest/EAVFramework.UnitTest.csproj index c365119..5781c0d 100644 --- a/test/EAVFramework.UnitTest/EAVFramework.UnitTest.csproj +++ b/test/EAVFramework.UnitTest/EAVFramework.UnitTest.csproj @@ -23,7 +23,8 @@ - + +