/// <summary> /// Extension method to use V3 to V4 translation middleware. /// </summary> /// <param name="builder">IApplicationBuilder that will use translation middleware</param> /// <param name="v3Edmx">V3 edmx to send back when requested for metadata</param> /// <param name="v4Model">Required V4 model to validate translated URI</param> /// <returns>builder now using migration middleware</returns> public static IApplicationBuilder UseODataMigration(this IApplicationBuilder builder, string v3Edmx, Microsoft.OData.Edm.IEdmModel v4Model) { return(builder .UseMiddleware <ODataMigrationMiddleware>(v3Edmx, v4Model) .UseRouter((new RouteBuilder(builder)).MapGet("$metadata", async(context) => { await context.Response.WriteAsync(v3Edmx); }).Build())); }
/// <inheritdoc/> public override ODataDeserializer GetODataDeserializer( Microsoft.OData.Edm.IEdmModel model, Type type, System.Net.Http.HttpRequestMessage request) { if (type == typeof(Uri)) { return(base.GetODataDeserializer(model, type, request)); } if (type == typeof(ODataActionParameters) || type == typeof(ODataUntypedActionParameters)) { return(new ExtendedODataActionPayloadDeserializer(Instance)); } return(new ExtendedODataEntityDeserializer(Instance)); }
/// <summary> /// Extension method to use V3 to V4 translation middleware. /// </summary> /// <param name="builder">IApplicationBuilder that will use translation middleware</param> /// <param name="v3model">V3 model that represents edmx contract</param> /// <param name="v4Model">Required V4 model to validate translated URI</param> /// <returns>builder now using migration middleware</returns> public static IApplicationBuilder UseODataMigration(this IApplicationBuilder builder, Data.Edm.IEdmModel v3model, Microsoft.OData.Edm.IEdmModel v4Model) { // Convert v3 model to string to pass through metadata request. string v3Edmx; using (StringWriter stringWriter = new StringWriter()) { using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter)) { IEnumerable <Data.Edm.Validation.EdmError> errors = new List <Data.Edm.Validation.EdmError>(); Data.Edm.Csdl.EdmxWriter.TryWriteEdmx(v3model, xmlWriter, Data.Edm.Csdl.EdmxTarget.OData, out errors); } v3Edmx = stringWriter.ToString(); } return(builder.UseODataMigration(v3Edmx, v4Model)); }
/// <summary> /// Constructs an instance of TranslationMiddleware, requiring the root of the service, a V3 model instance and V4 model instance. /// </summary> /// <param name="next">Delegate required for middleware</param> /// <param name="v3Edmx">V3 EDMX string representation of model</param> /// <param name="v4Model">Instance of V4 EDM model</param> public ODataMigrationMiddleware(RequestDelegate next, string v3Edmx, Microsoft.OData.Edm.IEdmModel v4Model) { ExceptionUtil.IfArgumentNullThrowException(v3Edmx, "v3Edmx", "V3 edmx cannot be empty"); ExceptionUtil.IfArgumentNullThrowException(v4Model, "v4Model", "V4 model cannot be null"); this.next = next; this.serviceRoot = new Uri("http://localhost/"); // The actual service root doesn't matter; it is just needed as a parameter this.v4Model = v4Model; try { using (XmlReader reader = XmlReader.Create(new StringReader(v3Edmx))) { this.v3Model = Data.Edm.Csdl.EdmxReader.Parse(reader); } } catch (Exception) { throw new ArgumentException("Unable to parse OData V3 Edmx; make sure your edmx is a valid OData contract."); } }
public static string GetPropertiestobeExpandedOn <T>(Microsoft.OData.Edm.IEdmModel metadata) where T : class { string expanededproperties = string.Empty; //get the object that the client has sent string[] arr1 = typeof(T).ToString().Split('.'); string objectpassed = arr1[arr1.Length - 1]; var type = metadata.FindDeclaredType("SG.MMS.EventSync.Data.Entities.odata." + objectpassed); Type myType = type.GetType(); IList <PropertyInfo> props = new List <PropertyInfo>(myType.GetProperties()); foreach (PropertyInfo prop in props) { object propValue = prop.GetValue(type, null); if (propValue != null) { var t = propValue.GetType().ToString(); if (t == "System.Collections.Generic.List`1[Microsoft.OData.Edm.IEdmProperty]") { var got = propValue as List <Microsoft.OData.Edm.IEdmProperty>; if (got.Exists(y => y.GetType().ToString() == "Microsoft.OData.Edm.Csdl.CsdlSemantics.CsdlSemanticsNavigationProperty"))//checks if navigation properties exist { var navproperties = got.FindAll(y => y.GetType().ToString() == "Microsoft.OData.Edm.Csdl.CsdlSemantics.CsdlSemanticsNavigationProperty"); navproperties.ForEach(x => { expanededproperties += x.Name + ","; }); } } } } return(expanededproperties); }
static void Main(string[] args) { bool getMetadata = true; // start by getting secrets. var builder = new ConfigurationBuilder() .AddEnvironmentVariables(); builder.AddUserSecrets <Program>(); var Configuration = builder.Build(); string csdl; // get the metadata. if (getMetadata) { csdl = GetDynamicsMetadata(Configuration); File.WriteAllText("dynamics-metadata.xml", csdl); } else { csdl = File.ReadAllText("dynamics-metadata.xml"); } // fix the csdl. csdl = csdl.Replace("ConcurrencyMode=\"Fixed\"", ""); Microsoft.OData.Edm.IEdmModel model = Microsoft.OData.Edm.Csdl.CsdlReader.Parse(XElement.Parse(csdl).CreateReader()); // fix dates. OpenApiTarget target = OpenApiTarget.Json; OpenApiWriterSettings settings = new OpenApiWriterSettings { BaseUri = new Uri(Configuration["DYNAMICS_ODATA_URI"]) }; string swagger = null; using (MemoryStream ms = new MemoryStream()) { model.WriteOpenApi(ms, target, settings); var buffer = ms.ToArray(); string temp = Encoding.UTF8.GetString(buffer, 0, buffer.Length); // The Microsoft OpenAPI.Net library doesn't seem to work with MS Dynamics metadata, so we use NSwag here. var runner = OpenApiDocument.FromJsonAsync(temp); runner.Wait(); var swaggerDocument = runner.Result; List <string> allops = new List <string>(); Dictionary <string, OpenApiPathItem> itemsToRemove = new Dictionary <string, OpenApiPathItem>(); // fix the operationIds. foreach (var operation in swaggerDocument.Operations) { string suffix = ""; if (operation.Method == OpenApiOperationMethod.Post) { suffix = "Create"; // for creates we also want to add a header parameter to ensure we get the new object back. OpenApiParameter swaggerParameter = new OpenApiParameter() { Type = JsonObjectType.String, Name = "Prefer", Default = "return=representation", Description = "Required in order for the service to return a JSON representation of the object.", Kind = OpenApiParameterKind.Header }; operation.Operation.Parameters.Add(swaggerParameter); } if (operation.Method == OpenApiOperationMethod.Patch) { suffix = "Update"; } if (operation.Method == OpenApiOperationMethod.Put) { suffix = "Put"; } if (operation.Method == OpenApiOperationMethod.Delete) { suffix = "Delete"; } if (operation.Method == OpenApiOperationMethod.Get) { if (operation.Path.Contains("{")) { suffix = "GetByKey"; } else { suffix = "Get"; } } string prefix = "Unknown"; string firstTag = operation.Operation.Tags.FirstOrDefault(); if (firstTag == null) { firstTag = operation.Path.Substring(1); } if (firstTag != null) { bool ok2Delete = true; string firstTagLower = firstTag.ToLower(); if (firstTagLower.Equals("contacts"))// || firstTagLower.Equals("accounts") ) //|| firstTagLower.Equals("invoices") || firstTagLower.Equals("sharepointsites") || firstTagLower.Equals("savedqueries") || firstTagLower.Equals("sharepointdocumentlocations")) { ok2Delete = false; } if (firstTagLower.IndexOf("test") != -1 && !firstTagLower.Contains("QuickTest") && !firstTagLower.Contains("UpdateStateValue") ) { ok2Delete = false; firstTagLower = firstTagLower.Replace("test_", ""); firstTagLower = firstTagLower.Replace("test", ""); operation.Operation.Tags.Clear(); operation.Operation.Tags.Add(firstTagLower); } if (ok2Delete) { if (!itemsToRemove.Keys.Contains(operation.Path)) { itemsToRemove.Add(operation.Path, operation.Operation.Parent); } } if (!allops.Contains(firstTag)) { allops.Add(firstTag); } prefix = firstTagLower; // Capitalize the first character. if (prefix.Length > 0) { prefix.Replace("test", ""); prefix = ("" + prefix[0]).ToUpper() + prefix.Substring(1); } // remove any underscores. prefix = prefix.Replace("_", ""); } operation.Operation.OperationId = prefix + "_" + suffix; // adjustments to operation parameters foreach (var parameter in operation.Operation.Parameters) { string name = parameter.Name; if (name == null) { name = parameter.ActualParameter.Name; } if (name != null) { if (name == "$top") { parameter.Kind = OpenApiParameterKind.Query; parameter.Reference = null; parameter.Schema = null; parameter.Type = JsonObjectType.Integer; } if (name == "$skip") { parameter.Kind = OpenApiParameterKind.Query; parameter.Reference = null; parameter.Schema = null; parameter.Type = JsonObjectType.Integer; } if (name == "$search") { parameter.Kind = OpenApiParameterKind.Query; parameter.Reference = null; parameter.Schema = null; parameter.Type = JsonObjectType.String; } if (name == "$filter") { parameter.Kind = OpenApiParameterKind.Query; parameter.Reference = null; parameter.Schema = null; parameter.Type = JsonObjectType.String; } if (name == "$count") { parameter.Kind = OpenApiParameterKind.Query; parameter.Reference = null; parameter.Schema = null; parameter.Type = JsonObjectType.Boolean; } if (name == "If-Match") { parameter.Reference = null; parameter.Schema = null; } if (string.IsNullOrEmpty(parameter.Name)) { parameter.Name = name; } } //var parameter = loopParameter.ActualParameter; // get rid of style if it exists. if (parameter.Style != OpenApiParameterStyle.Undefined) { parameter.Style = OpenApiParameterStyle.Undefined; } // clear unique items if (parameter.UniqueItems) { parameter.UniqueItems = false; } // we also need to align the schema if it exists. if (parameter.Schema != null && parameter.Schema.Item != null) { var schema = parameter.Schema; if (schema.Type == JsonObjectType.Array) { // move schema up a level. parameter.Item = schema.Item; parameter.Schema = null; parameter.Reference = null; parameter.Type = JsonObjectType.Array; } if (schema.Type == JsonObjectType.String) { parameter.Schema = null; parameter.Reference = null; parameter.Type = JsonObjectType.String; } } else { // many string parameters don't have the type defined. if (!(parameter.Kind == OpenApiParameterKind.Body) && !parameter.HasReference && (parameter.Type == JsonObjectType.Null || parameter.Type == JsonObjectType.None)) { parameter.Schema = null; parameter.Type = JsonObjectType.String; } } } // adjustments to response foreach (var response in operation.Operation.Responses) { var val = response.Value; if (val != null && val.Reference == null && val.Schema != null) { bool hasValue = false; var schema = val.Schema; foreach (var property in schema.Properties) { if (property.Key.Equals("value")) { hasValue = true; break; } } if (hasValue) { string resultName = operation.Operation.OperationId + "ResponseModel"; if (!swaggerDocument.Definitions.ContainsKey(resultName)) { // move the inline schema to defs. swaggerDocument.Definitions.Add(resultName, val.Schema); val.Schema = new JsonSchema(); val.Schema.Reference = swaggerDocument.Definitions[resultName]; val.Schema.Type = JsonObjectType.None; } // ODATA - experimental //operation.Operation.ExtensionData.Add ("x-ms-odata", "#/definitions/") } /* * var schema = val.Schema; * * foreach (var property in schema.Properties) * { * if (property.Key.Equals("value")) * { * property.Value.ExtensionData.Add("x-ms-client-flatten", true); * } * } */ } } } foreach (var opDelete in itemsToRemove) { swaggerDocument.Paths.Remove(opDelete); } /* * foreach (var path in swaggerDocument.Paths) * { * foreach (var parameter in path.Value.Parameters) * { * var name = parameter.Name; * } * } */ /* * Cleanup definitions. */ foreach (var definition in swaggerDocument.Definitions) { foreach (var property in definition.Value.Properties) { // fix for doubles if (property.Value != null && property.Value.Format != null && property.Value.Format.Equals("double")) { property.Value.Format = "decimal"; } if (property.Key.Equals("adoxio_birthdate")) { property.Value.Format = "date"; } if (property.Key.Equals("totalamount")) { property.Value.Type = JsonObjectType.Number; property.Value.Format = "decimal"; } if (property.Key.Equals("versionnumber")) { // clear oneof. property.Value.OneOf.Clear(); // force to string. property.Value.Type = JsonObjectType.String; } } } // cleanup parameters. swaggerDocument.Parameters.Clear(); /* * swagger = swaggerDocument.ToJson(SchemaType.Swagger2); * * // fix up the swagger file. * * swagger = swagger.Replace("('{", "({"); * swagger = swagger.Replace("}')", "})"); * * swagger = swagger.Replace("\"$ref\": \"#/responses/error\"", "\"schema\": { \"$ref\": \"#/definitions/odata.error\" }"); * * // fix for problem with the base entity. * * swagger = swagger.Replace(" {\r\n \"$ref\": \"#/definitions/Microsoft.Dynamics.CRM.crmbaseentity\"\r\n },\r\n", ""); * * // fix for problem with nullable string arrays. * swagger = swagger.Replace(",\r\n \"nullable\": true", ""); * // NSwag is almost able to generate the client as well. It does it much faster than AutoRest but unfortunately can't do multiple files yet. * // It does generate a single very large file in a few seconds. We don't want a single very large file though as it does not work well with the IDE. * // Autorest is used to generate the client using a traditional approach. * */ var generatorSettings = new CSharpClientGeneratorSettings { ClassName = "DynamicsClient", CSharpGeneratorSettings = { Namespace = "Dynamics" } }; var generator = new CSharpClientGenerator(swaggerDocument, generatorSettings); var code = generator.GenerateFile(); File.WriteAllText("DynamicsClient.cs", code); } //File.WriteAllText("dynamics-swagger.json", swagger); }
static void Main(string[] args) { bool getMetadata = true; // start by getting secrets. var builder = new ConfigurationBuilder() .AddEnvironmentVariables(); builder.AddUserSecrets <Program>(); var Configuration = builder.Build(); string csdl; // get the metadata. if (getMetadata) { csdl = GetDynamicsMetadata(Configuration); File.WriteAllText("dynamics-metadata.xml", csdl); } else { csdl = File.ReadAllText("dynamics-metadata.xml"); } // fix the csdl. csdl = csdl.Replace("ConcurrencyMode=\"Fixed\"", ""); Microsoft.OData.Edm.IEdmModel model = Microsoft.OData.Edm.Csdl.CsdlReader.Parse(XElement.Parse(csdl).CreateReader()); // fix dates. OpenApiTarget target = OpenApiTarget.Json; OpenApiWriterSettings settings = new OpenApiWriterSettings { BaseUri = new Uri(Configuration["DYNAMICS_ODATA_URI"]) }; string swagger = null; using (MemoryStream ms = new MemoryStream()) { model.WriteOpenApi(ms, target, settings); var buffer = ms.ToArray(); string temp = Encoding.UTF8.GetString(buffer, 0, buffer.Length); // The Microsoft OpenAPI.Net library doesn't seem to work with MS Dynamics metadata, so we use NSwag here. var runner = SwaggerDocument.FromJsonAsync(temp); runner.Wait(); var swaggerDocument = runner.Result; List <string> allops = new List <string>(); Dictionary <string, SwaggerPathItem> itemsToRemove = new Dictionary <string, SwaggerPathItem>(); // fix the operationIds. foreach (var operation in swaggerDocument.Operations) { string suffix = ""; switch (operation.Method) { case SwaggerOperationMethod.Post: suffix = "Create"; // for creates we also want to add a header parameter to ensure we get the new object back. SwaggerParameter swaggerParameter = new SwaggerParameter() { Type = JsonObjectType.String, Name = "Prefer", Default = "return=representation", Description = "Required in order for the service to return a JSON representation of the object.", Kind = SwaggerParameterKind.Header }; operation.Operation.Parameters.Add(swaggerParameter); break; case SwaggerOperationMethod.Patch: suffix = "Update"; break; case SwaggerOperationMethod.Put: suffix = "Put"; break; case SwaggerOperationMethod.Delete: suffix = "Delete"; break; case SwaggerOperationMethod.Get: if (operation.Path.Contains("{")) { suffix = "GetByKey"; } else { suffix = "Get"; } break; } string prefix = "Unknown"; string firstTag = operation.Operation.Tags.FirstOrDefault(); if (firstTag == null) { firstTag = operation.Path.Substring(1); } if (firstTag != null) { bool ok2Delete = true; string firstTagLower = firstTag.ToLower(); // || firstTagLower.Equals("equipments") if (firstTagLower.Equals("incidents") || firstTagLower.Equals("sharepointdocumentlocations") || firstTagLower.Equals("sharepointsites") || firstTagLower.Equals("contacts") || firstTagLower.Equals("accounts") || firstTagLower.Equals("invoices") || firstTagLower.Equals("entitydefinitions") || firstTagLower.Equals("globaloptionsetdefinitions")) { ok2Delete = false; } if (firstTagLower.IndexOf("bcgov") != -1) { ok2Delete = false; firstTagLower = firstTagLower.Replace("bcgov_", ""); firstTagLower = firstTagLower.Replace("bcgov", ""); operation.Operation.Tags.Clear(); operation.Operation.Tags.Add(firstTagLower); } if (ok2Delete) { if (!itemsToRemove.Keys.Contains(operation.Path)) { itemsToRemove.Add(operation.Path, operation.Operation.Parent); } } if (!allops.Contains(firstTag)) { allops.Add(firstTag); } prefix = firstTagLower; // Capitalize the first character. if (prefix.Length > 0) { prefix.Replace("bcgov", ""); prefix = ("" + prefix[0]).ToUpper() + prefix.Substring(1); } // remove any underscores. prefix = prefix.Replace("_", ""); } operation.Operation.OperationId = prefix + "_" + suffix; // adjustments to operation parameters foreach (var parameter in operation.Operation.Parameters) { string name = parameter.Name; if (name == null) { name = parameter.ActualParameter.Name; } if (name != null) { if (name == "$top") { parameter.Kind = SwaggerParameterKind.Query; parameter.Reference = null; parameter.Schema = null; parameter.Type = JsonObjectType.Integer; } if (name == "$skip") { parameter.Kind = SwaggerParameterKind.Query; parameter.Reference = null; parameter.Schema = null; parameter.Type = JsonObjectType.Integer; } if (name == "$search") { parameter.Kind = SwaggerParameterKind.Query; parameter.Reference = null; parameter.Schema = null; parameter.Type = JsonObjectType.String; } if (name == "$filter") { parameter.Kind = SwaggerParameterKind.Query; parameter.Reference = null; parameter.Schema = null; parameter.Type = JsonObjectType.String; } if (name == "$count") { parameter.Kind = SwaggerParameterKind.Query; parameter.Reference = null; parameter.Schema = null; parameter.Type = JsonObjectType.Boolean; } if (name == "If-Match") { parameter.Reference = null; parameter.Schema = null; } if (string.IsNullOrEmpty(parameter.Name)) { parameter.Name = name; } } //var parameter = loopParameter.ActualParameter; // get rid of style if it exists. if (parameter.Style != SwaggerParameterStyle.Undefined) { parameter.Style = SwaggerParameterStyle.Undefined; } // clear unique items if (parameter.UniqueItems) { parameter.UniqueItems = false; } // we also need to align the schema if it exists. if (parameter.Schema != null && parameter.Schema.Item != null) { var schema = parameter.Schema; if (schema.Type == JsonObjectType.Array) { // move schema up a level. parameter.Item = schema.Item; parameter.Schema = null; parameter.Reference = null; parameter.Type = JsonObjectType.Array; } if (schema.Type == JsonObjectType.String) { parameter.Schema = null; parameter.Reference = null; parameter.Type = JsonObjectType.String; } } else { // many string parameters don't have the type defined. if (!(parameter.Kind == SwaggerParameterKind.Body) && !parameter.HasReference && (parameter.Type == JsonObjectType.Null || parameter.Type == JsonObjectType.None)) { parameter.Schema = null; parameter.Type = JsonObjectType.String; } } } // adjustments to response // changes to fix the “GetOkResponseModelModelModel…” generated models // Based on Cannabis solution https://github.com/bcgov/jag-lcrb-carla-public/blob/master/cllc-interfaces/OData.OpenAPI/odata2openapi/Program.cs line 342 foreach (var response in operation.Operation.Responses) { var val = response.Value; if (val != null && val.Reference == null && val.Schema != null) { bool hasValue = false; var schema = val.Schema; foreach (var property in val.Schema.Properties) { if (property.Key.Equals("value")) { hasValue = true; break; } } if (hasValue) { string resultName = operation.Operation.OperationId + "ResponseModel"; if (!swaggerDocument.Definitions.ContainsKey(resultName)) { // move the inline schema to defs. swaggerDocument.Definitions.Add(resultName, val.Schema); val.Schema = new JsonSchema4(); val.Schema.Reference = swaggerDocument.Definitions[resultName]; val.Schema.Type = JsonObjectType.None; } } } } // end of adjustments to response } foreach (var opDelete in itemsToRemove) { Debug.WriteLine($"Removing {opDelete.Key}"); swaggerDocument.Paths.Remove(opDelete); } /* * Cleanup definitions. */ foreach (var definition in swaggerDocument.Definitions) { foreach (var property in definition.Value.Properties) { if (property.Key.Equals("totalamount")) { property.Value.Type = JsonObjectType.Number; property.Value.Format = "decimal"; } if (property.Key.Equals("versionnumber")) { // clear oneof. property.Value.OneOf.Clear(); // force to string. property.Value.Type = JsonObjectType.String; } } } // cleanup parameters. swaggerDocument.Parameters.Clear(); swagger = swaggerDocument.ToJson(SchemaType.Swagger2); // fix up the swagger file. swagger = swagger.Replace("('{", "({"); swagger = swagger.Replace("}')", "})"); swagger = swagger.Replace("\"$ref\": \"#/responses/error\"", "\"schema\": { \"$ref\": \"#/definitions/odata.error\" }"); // fix for problem with the base entity. swagger = swagger.Replace(" {\r\n \"$ref\": \"#/definitions/Microsoft.Dynamics.CRM.crmbaseentity\"\r\n },\r\n", ""); // NSwag is almost able to generate the client as well. It does it much faster than AutoRest but unfortunately can't do multiple files yet. /* * var generatorSettings = new SwaggerToCSharpClientGeneratorSettings * { * ClassName = "DynamicsClient", * CSharpGeneratorSettings = * { * Namespace = "<namespace>" * } * }; * * var generator = new SwaggerToCSharpClientGenerator(swaggerDocument, generatorSettings); * var code = generator.GenerateFile(); * * File.WriteAllText("<filename>", code); */ } // output the file. File.WriteAllText("dynamics-swagger.json", swagger); }
static void Main(string[] args) { bool getMetadata = false; // start by getting secrets. var builder = new ConfigurationBuilder() .AddEnvironmentVariables(); builder.AddUserSecrets <Program>(); var Configuration = builder.Build(); string csdl; // get the metadata. if (getMetadata) { csdl = GetDynamicsMetadata(Configuration); File.WriteAllText("C:\\tmp\\dynamics-metadata.xml", csdl); } else { csdl = File.ReadAllText("C:\\tmp\\dynamics-metadata.xml"); } // fix the csdl. csdl = csdl.Replace("ConcurrencyMode=\"Fixed\"", ""); Microsoft.OData.Edm.IEdmModel model = Microsoft.OData.Edm.Csdl.CsdlReader.Parse(XElement.Parse(csdl).CreateReader()); OpenApiTarget target = OpenApiTarget.Json; OpenApiWriterSettings settings = new OpenApiWriterSettings { BaseUri = new Uri(Configuration["DYNAMICS_ODATA_URI"]) }; string swagger = null; using (MemoryStream ms = new MemoryStream()) { model.WriteOpenApi(ms, target, settings); var buffer = ms.ToArray(); string temp = Encoding.UTF8.GetString(buffer, 0, buffer.Length); // The Microsoft OpenAPI.Net library doesn't seem to work with MS Dynamics metadata, so we use NSwag here. var runner = SwaggerDocument.FromJsonAsync(temp); runner.Wait(); var swaggerDocument = runner.Result; // fix the operationIds. foreach (var operation in swaggerDocument.Operations) { string suffix = ""; switch (operation.Method) { case SwaggerOperationMethod.Post: suffix = "Create"; // for creates we also want to add a header parameter to ensure we get the new object back. SwaggerParameter swaggerParameter = new SwaggerParameter() { Type = JsonObjectType.String, Name = "Prefer", Default = "return=representation", Description = "Required in order for the service to return a JSON representation of the object.", Kind = SwaggerParameterKind.Header }; break; case SwaggerOperationMethod.Patch: suffix = "Update"; break; case SwaggerOperationMethod.Put: suffix = "Put"; break; case SwaggerOperationMethod.Delete: suffix = "Delete"; break; case SwaggerOperationMethod.Get: if (operation.Path.Contains("{")) { suffix = "GetByKey"; } else { suffix = "Get"; } break; } string prefix = "Unknown"; string firstTag = operation.Operation.Tags.FirstOrDefault(); if (firstTag != null) { prefix = firstTag; // Capitalize the first character. if (prefix.Length > 0) { prefix = ("" + prefix[0]).ToUpper() + prefix.Substring(1); } // remove any underscores. prefix = prefix.Replace("_", ""); } operation.Operation.OperationId = prefix + "_" + suffix; // adjustments to operation parameters foreach (var parameter in operation.Operation.Parameters) { // get rid of style if it exists. if (parameter.Style != SwaggerParameterStyle.Undefined) { parameter.Style = SwaggerParameterStyle.Undefined; } // clear unique items if (parameter.UniqueItems) { parameter.UniqueItems = false; } // many string parameters don't have the type defined. if (parameter.Type == JsonObjectType.Null) { parameter.Type = JsonObjectType.String; } // we also need to align the schema if it exists. if (parameter.Schema != null) { var schema = parameter.Schema; if (schema.Type == JsonObjectType.Array) { // move schema up a level. parameter.Items.Clear(); foreach (var schemaItem in schema.Items) { parameter.Items.Add(schemaItem); } parameter.Schema = null; parameter.Type = JsonObjectType.Array; } } } } swagger = swaggerDocument.ToJson(SchemaType.Swagger2); // fix up the swagger file. swagger = swagger.Replace("('{", "({"); swagger = swagger.Replace("}')", "})"); // NSwag is almost able to generate the client as well. It does it much faster than AutoRest but unfortunately can't do multiple files yet. // It does generate a single very large file in a few seconds. We don't want a single very large file though as it does not work well with the IDE. // Autorest is used to generate the client using a traditional approach. /* * var generatorSettings = new SwaggerToCSharpClientGeneratorSettings * { * ClassName = "DynamicsClient", * CSharpGeneratorSettings = * { * Namespace = "<namespace>" * } * }; * * var generator = new SwaggerToCSharpClientGenerator(swaggerDocument, generatorSettings); * var code = generator.GenerateFile(); * * File.WriteAllText("<filename>", code); */ } File.WriteAllText("C:\\tmp\\dynamics-swagger.json", swagger); Console.Out.WriteLine(swagger); }