diff --git a/src/NetCoreForce.ModelGenerator/GenConfig.cs b/src/NetCoreForce.ModelGenerator/GenConfig.cs index 979a311..5570112 100644 --- a/src/NetCoreForce.ModelGenerator/GenConfig.cs +++ b/src/NetCoreForce.ModelGenerator/GenConfig.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using NetCoreForce.Client.Models; @@ -12,8 +13,25 @@ public class GenConfig public string ClassPrefix { get; set; } public string ClassSuffix { get; set; } public string ClassNamespace { get; set; } + + /// + /// Add namespace using + /// + public bool AddNetCoreForceModelsUsings { get; set; } public bool IncludeCustom { get; set; } public bool IncludeReferences { get; set; } + + + public IDictionary Mappings { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public ObjectMappingConfig Mapping(string objectName) + { + ObjectMappingConfig objectMapping = null; + + Mappings?.TryGetValue(objectName, out objectMapping); + + return objectMapping ?? new ObjectMappingConfig(); + } public GenConfig() { diff --git a/src/NetCoreForce.ModelGenerator/MappingConfig.cs b/src/NetCoreForce.ModelGenerator/MappingConfig.cs new file mode 100644 index 0000000..19eb4a6 --- /dev/null +++ b/src/NetCoreForce.ModelGenerator/MappingConfig.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +namespace NetCoreForce.ModelGenerator +{ + /// + /// Mapping configuration + /// + public class ObjectMappingConfig + { + /// + /// Used to override the class name (or null). + /// + public string Name { get; set; } + + /// + /// If true, don't generate the class for this object type (and the relationship to this type too). + /// + public bool Ignore { get; set; } + + /// + /// The mappings for the fields of this object type. + /// + public Dictionary Fields { get; set; } = new Dictionary(); + + + private List _regexFieldsMappings = null; + private List RegexFieldMappings + { + get + { + if (_regexFieldsMappings == null) + _regexFieldsMappings = Fields.Select(kvp => new RegexFieldMappingConfig(kvp.Key, kvp.Value)).ToList(); + + return _regexFieldsMappings; + } + } + + /// + /// Get the which it's regex matches the field name given. + /// + /// The field name + /// The which it's regex matches the field name given or null. + public FieldMappingConfig Field(string fieldName) + { + FieldMappingConfig mapping = null; + + if (!string.IsNullOrWhiteSpace(fieldName)) + mapping = RegexFieldMappings.FirstOrDefault(rfb => rfb.IsMatch(fieldName))?.Mapping; + + return mapping ?? new FieldMappingConfig(); + } + + /// + /// Bind a with a . + /// + private class RegexFieldMappingConfig + { + public RegexFieldMappingConfig(string regexPattern, FieldMappingConfig mapping) + { + Regex = new Regex(regexPattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + Mapping = mapping; + } + + public Regex Regex { get; } + public FieldMappingConfig Mapping { get; } + + public bool IsMatch(string fieldName) => Regex.IsMatch(fieldName); + } + + /// + /// Field mapping configuration + /// + public class FieldMappingConfig + { + /// + /// Override the field name with this name (if null, keep field name) + /// + public string Name { get; set; } + + /// + /// Ignore this field + /// + public bool Ignore { get; set; } + } + } +} \ No newline at end of file diff --git a/src/NetCoreForce.ModelGenerator/Program.cs b/src/NetCoreForce.ModelGenerator/Program.cs index 611f802..5f79f52 100644 --- a/src/NetCoreForce.ModelGenerator/Program.cs +++ b/src/NetCoreForce.ModelGenerator/Program.cs @@ -378,6 +378,13 @@ private static async Task GenModels(GenConfig config) Console.WriteLine("Output directory: " + config.OutputDirectory); + if (!Directory.Exists(config.OutputDirectory)) + { + Directory.CreateDirectory(config.OutputDirectory); + + Console.WriteLine("Output directory created : " + config.OutputDirectory); + } + bool generateAll = false; if (config.Objects != null && config.Objects.Count > 0) @@ -408,7 +415,7 @@ private static async Task GenModels(GenConfig config) { if (config.Objects != null && config.Objects.Count > 0) { - bool incl = config.Objects.Where(o => o.ToLowerInvariant() == obj.Name.ToLowerInvariant()).Count() > 0; + bool incl = config.Objects.Any(o => o.ToLowerInvariant() == obj.Name.ToLowerInvariant()); if (!incl) { #if DEBUG @@ -423,7 +430,8 @@ private static async Task GenModels(GenConfig config) Console.Write("Generating model for {0} - ", obj.Name); - string className = obj.Name; + var classMapping = config.Mapping(obj.Name); + var className = classMapping.Name ?? obj.Name; className = string.Format("{0}{1}{2}", config.ClassPrefix ?? string.Empty, className, config.ClassSuffix ?? string.Empty); @@ -463,20 +471,30 @@ public static async Task GenClass(ForceClient client, string objectName, string newline = Environment.NewLine; gen.AppendLine("using System;"); + gen.AppendLine("using System.CodeDom.Compiler;"); gen.AppendLine("using NetCoreForce.Client.Models;"); gen.AppendLine("using NetCoreForce.Client.Attributes;"); + + if (config.AddNetCoreForceModelsUsings) + gen.AppendLine("using NetCoreForce.Models;"); + gen.AppendLine("using Newtonsoft.Json;"); + gen.AppendLine(); if (!string.IsNullOrEmpty(config.ClassNamespace)) { gen.AppendLine("namespace " + config.ClassNamespace); gen.AppendLine("{"); } + + var assembly = Assembly.GetEntryAssembly().GetName(); + gen.AppendLine("\t///"); gen.AppendLine($"\t/// {WebUtility.HtmlEncode(data.Label)}"); gen.AppendLine($"\t///SObject Name: {data.Name}"); gen.AppendLine($"\t///Custom Object: {data.Custom.ToString()}"); gen.AppendLine("\t///"); + gen.AppendLine($"\t[GeneratedCodeAttribute(\"{typeof(Program).Namespace}\", \"{assembly.Version}\")]"); gen.AppendLine($"\tpublic class {className} : SObject"); gen.AppendLine("\t{"); @@ -492,14 +510,16 @@ public static async Task GenClass(ForceClient client, string objectName, // gen.AppendLine("\t\t{}"); // gen.AppendLine(); + var classMapping = config.Mapping(objectName); + foreach (var field in data.Fields) { try { - if (field.Custom && !config.IncludeCustom) - { + var fieldMapping = classMapping.Field(field.Name); + + if (field.Custom && !config.IncludeCustom || fieldMapping.Ignore) continue; - } gen.AppendLine("\t\t///"); gen.AppendLine("\t\t/// " + WebUtility.HtmlEncode(field.Label)); @@ -519,7 +539,8 @@ public static async Task GenClass(ForceClient client, string objectName, gen.AppendLine("\t\t///"); - gen.AppendLine(string.Format("\t\t[JsonProperty(PropertyName = \"{0}\")]", JsonName(field.Name))); + var jsonName = JsonName(field.Name); + gen.AppendLine(string.Format("\t\t[JsonProperty(PropertyName = \"{0}\")]", jsonName)); if (!field.Creatable || !field.Updateable) { @@ -555,12 +576,14 @@ public static async Task GenClass(ForceClient client, string objectName, csTypeName += "?"; } - gen.AppendLine(string.Format("\t\tpublic {0} {1} {{ get; set; }}", csTypeName, field.Name)); + gen.AppendLine(string.Format("\t\tpublic {0} {1} {{ get; set; }}", csTypeName, fieldMapping.Name ?? field.Name)); gen.AppendLine(); if (field.Type == "reference" && config.IncludeReferences) { - if (string.IsNullOrEmpty(field.RelationshipName) || field.ReferenceTo.Count > 1) + var relationshipfieldMapping = classMapping.Field(field.RelationshipName); + + if (string.IsNullOrEmpty(field.RelationshipName) || field.ReferenceTo.Count > 1 || relationshipfieldMapping.Ignore) { //only do single-object relationships continue; @@ -572,6 +595,16 @@ public static async Task GenClass(ForceClient client, string objectName, continue; } + var targetMapping = config.Mapping(field.ReferenceTo[0]); + + if (targetMapping.Ignore) // if we ignore the target class --> ignore the relationship + continue; + + string referenceClass = GetPrefixedSuffixed(config, targetMapping.Name ?? field.ReferenceTo[0]); + + if (!string.IsNullOrEmpty(config.ClassNamespace) && config.AddNetCoreForceModelsUsings) + referenceClass = config.ClassNamespace + "." + referenceClass; + gen.AppendLine("\t\t///"); gen.AppendLine("\t\t/// ReferenceTo: " + field.ReferenceTo[0]); gen.AppendLine("\t\t/// RelationshipName: " + field.RelationshipName + ""); @@ -579,9 +612,9 @@ public static async Task GenClass(ForceClient client, string objectName, gen.AppendLine(string.Format("\t\t[JsonProperty(PropertyName = \"{0}\")]", JsonName(field.RelationshipName))); gen.AppendLine("\t\t[Updateable(false), Createable(false)]"); - string referenceClass = GetPrefixedSuffixed(config, field.ReferenceTo[0]); + - gen.AppendLine(string.Format("\t\tpublic {0} {1} {{ get; set; }}", referenceClass, field.RelationshipName)); + gen.AppendLine(string.Format("\t\tpublic {0} {1} {{ get; set; }}", referenceClass, relationshipfieldMapping.Name ?? field.RelationshipName)); gen.AppendLine(); } } diff --git a/src/NetCoreForce.ModelGenerator/README.md b/src/NetCoreForce.ModelGenerator/README.md index 8105017..5b34339 100644 --- a/src/NetCoreForce.ModelGenerator/README.md +++ b/src/NetCoreForce.ModelGenerator/README.md @@ -82,10 +82,65 @@ No configuration file is required, however you can include the --save-config opt "ClassSuffix": null, "ClassNamespace": "NetCoreForce.Models", "IncludeCustom": true, - "IncludeReferences": true + "IncludeReferences": true, + "AddNetCoreForceModelsUsings": true, } ``` **Generating all objects at once:** If you wish to generate all queryable objects in the generated output, add "all" as the first or only item in the "Objects" array in the config file, or enter "all" (without quotes) when prompted in the console. **Note:** if you use the -r/--include-references option, you may run into compile errors with missing classes. For instance, the Salesforce "User" object appears on many objects - if you didnt include this in your list of models to generate, you will either need to include it, or manually remove those properties from the generated classes. + + +## Mappings configuration + +Features : + - Rename a class + - Ignore a class generation (and all its referencing relationships) + - Rename a field in a class + - Ignore a field in a class + - Ignore all fields matching a regex in a class + +### Example config file + +```json +{ + /// ... + + //Optional + "Mappings": { + + //Rename Object_Name to ObjectName + "object_name": { + "Name": "ObjectName" + }, + + //Ignore Object_Name_To_Ignore type generation + // (+ ignore all relationships to this Object_Name_To_Ignore) + "Object_Name_To_Ignore": { + "Ignore": true + }, + + //Configure object fields + "Object_name_config_fields": { + "Fields": { + + // rename field during generation + "old_name": { + "Name": "NewName" + }, + + // ignore field + "field_to_ignore": { + "Ignore": true + }, + + // ignore multile fields matching a regex (eg: starting with 'billing') + "billing.*": { + "Ignore": true + } + } + } + } + +}``` \ No newline at end of file diff --git a/src/NetCoreForce.ModelGenerator/modelgenerator_config_example.json b/src/NetCoreForce.ModelGenerator/modelgenerator_config_example.json index 2cc75cb..59e5826 100755 Binary files a/src/NetCoreForce.ModelGenerator/modelgenerator_config_example.json and b/src/NetCoreForce.ModelGenerator/modelgenerator_config_example.json differ