Пример #1
0
 /// <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));
        }
Пример #3
0
        /// <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.");
            }
        }
Пример #5
0
        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);
        }
Пример #6
0
        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);
        }
Пример #7
0
        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);
        }
Пример #8
0
        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);
        }