Esempio n. 1
0
        /// <summary>
        /// Serializes the <see cref="IOpenApiSerializable"/> to the Open API document using
        /// the given stream, specification version and the format.
        /// </summary>
        /// <typeparam name="T">the <see cref="IOpenApiSerializable"/></typeparam>
        /// <param name="element">The Open API element.</param>
        /// <param name="stream">The given stream.</param>
        /// <param name="specVersion">The Open API specification version.</param>
        /// <param name="format">The output format (JSON or YAML).</param>
        /// <param name="settings">Provide configuration settings for controlling writing output</param>
        public static void Serialize <T>(
            this T element,
            Stream stream,
            OpenApiSpecVersion specVersion,
            OpenApiFormat format,
            OpenApiWriterSettings settings = null)
            where T : IOpenApiSerializable
        {
            if (stream == null)
            {
                throw Error.ArgumentNull(nameof(stream));
            }

            IOpenApiWriter writer;
            var            streamWriter = new FormattingStreamWriter(stream, CultureInfo.InvariantCulture);

            switch (format)
            {
            case OpenApiFormat.Json:
                writer = new OpenApiJsonWriter(streamWriter, settings);
                break;

            case OpenApiFormat.Yaml:
                writer = new OpenApiYamlWriter(streamWriter, settings);
                break;

            default:
                throw new OpenApiException(string.Format(SRResource.OpenApiFormatNotSupported, format));
            }

            element.Serialize(writer, specVersion);
        }
        private static string WriteEdmModelToOpenApi(IEdmModel model, OpenApiTarget target,
                                                     OpenApiWriterSettings settings = null)
        {
            MemoryStream stream = new MemoryStream();

            model.WriteOpenApi(stream, target, settings);
            stream.Flush();
            stream.Position = 0;
            return(new StreamReader(stream).ReadToEnd());
        }
        public void TripServiceMetadataToOpenApiYamlWorks()
        {
            // Arrange
            IEdmModel             model    = EdmModelHelper.TripServiceModel;
            OpenApiWriterSettings settings = new OpenApiWriterSettings
            {
                Version = new Version(1, 0, 1),
                BaseUri = new Uri("http://services.odata.org/TrippinRESTierService/")
            };

            // Act
            string yaml = WriteEdmModelToOpenApi(model, OpenApiTarget.Yaml, settings);

            // Assert
            Assert.Equal(Resources.GetString("TripService.OpenApi.yaml").Replace(), yaml);
        }
Esempio n. 4
0
        /// <summary>
        /// Generate the Open Api.
        /// </summary>
        public bool Generate()
        {
            try
            {
                IEdmModel edmModel = GetEdmModel();

                OpenApiWriterSettings settings = GetSettings();

                using (FileStream fs = File.Create(Output))
                {
                    edmModel.WriteOpenApi(fs, Target, settings);
                    fs.Flush();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return(false);
            }

            return(true);
        }
Esempio n. 5
0
        public static void ProcessOpenApiDocument(
            FileInfo input,
            FileInfo output,
            OpenApiSpecVersion version,
            OpenApiFormat format,
            bool inline)
        {
            OpenApiDocument document;

            using (Stream stream = input.OpenRead())
            {
                document = new OpenApiStreamReader(new OpenApiReaderSettings
                {
                    ReferenceResolution = ReferenceResolutionSetting.ResolveLocalReferences,
                    RuleSet             = ValidationRuleSet.GetDefaultRuleSet()
                }
                                                   ).Read(stream, out var context);
                if (context.Errors.Count != 0)
                {
                    var errorReport = new StringBuilder();

                    foreach (var error in context.Errors)
                    {
                        errorReport.AppendLine(error.ToString());
                    }

                    throw new ArgumentException(String.Join(Environment.NewLine, context.Errors.Select(e => e.Message).ToArray()));
                }
            }

            using (var outputStream = output?.Create())
            {
                TextWriter textWriter;

                if (outputStream != null)
                {
                    textWriter = new StreamWriter(outputStream);
                }
                else
                {
                    textWriter = Console.Out;
                }

                var settings = new OpenApiWriterSettings()
                {
                    ReferenceInline = inline == true ? ReferenceInlineSetting.InlineLocalReferences : ReferenceInlineSetting.DoNotInlineReferences
                };
                IOpenApiWriter writer;
                switch (format)
                {
                case OpenApiFormat.Json:
                    writer = new OpenApiJsonWriter(textWriter, settings);
                    break;

                case OpenApiFormat.Yaml:
                    writer = new OpenApiYamlWriter(textWriter, settings);
                    break;

                default:
                    throw new ArgumentException("Unknown format");
                }

                document.Serialize(writer, version);

                textWriter.Flush();
            }
        }
Esempio n. 6
0
        public static void ProcessOpenApiDocument(
            string input,
            FileInfo output,
            OpenApiSpecVersion version,
            OpenApiFormat format,
            string filterByOperationIds,
            string filterByTags,
            bool inline,
            bool resolveExternal)
        {
            if (string.IsNullOrEmpty(input))
            {
                throw new ArgumentNullException(nameof(input));
            }
            if (output == null)
            {
                throw new ArgumentException(nameof(output));
            }
            if (output.Exists)
            {
                throw new IOException("The file you're writing to already exists. Please input a new output path.");
            }

            var stream = GetStream(input);
            var result = new OpenApiStreamReader(new OpenApiReaderSettings
            {
                ReferenceResolution = resolveExternal ? ReferenceResolutionSetting.ResolveAllReferences : ReferenceResolutionSetting.ResolveLocalReferences,
                RuleSet             = ValidationRuleSet.GetDefaultRuleSet()
            }
                                                 ).ReadAsync(stream).GetAwaiter().GetResult();

            OpenApiDocument document;

            document = result.OpenApiDocument;

            // Check if filter options are provided, then execute
            if (!string.IsNullOrEmpty(filterByOperationIds) && !string.IsNullOrEmpty(filterByTags))
            {
                throw new InvalidOperationException("Cannot filter by operationIds and tags at the same time.");
            }

            if (!string.IsNullOrEmpty(filterByOperationIds))
            {
                var predicate = OpenApiFilterService.CreatePredicate(operationIds: filterByOperationIds);
                document = OpenApiFilterService.CreateFilteredDocument(document, predicate);
            }
            if (!string.IsNullOrEmpty(filterByTags))
            {
                var predicate = OpenApiFilterService.CreatePredicate(tags: filterByTags);
                document = OpenApiFilterService.CreateFilteredDocument(document, predicate);
            }

            var context = result.OpenApiDiagnostic;

            if (context.Errors.Count > 0)
            {
                var errorReport = new StringBuilder();

                foreach (var error in context.Errors)
                {
                    errorReport.AppendLine(error.ToString());
                }

                throw new ArgumentException(string.Join(Environment.NewLine, context.Errors.Select(e => e.Message).ToArray()));
            }

            using var outputStream = output?.Create();

            var textWriter = outputStream != null ? new StreamWriter(outputStream) : Console.Out;

            var settings = new OpenApiWriterSettings()
            {
                ReferenceInline = inline ? ReferenceInlineSetting.InlineLocalReferences : ReferenceInlineSetting.DoNotInlineReferences
            };
            IOpenApiWriter writer = format switch
            {
                OpenApiFormat.Json => new OpenApiJsonWriter(textWriter, settings),
                OpenApiFormat.Yaml => new OpenApiYamlWriter(textWriter, settings),
                _ => throw new ArgumentException("Unknown format"),
            };

            document.Serialize(writer, version);

            textWriter.Flush();
        }
Esempio n. 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 = 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);
        }
Esempio n. 8
0
        public static async void ProcessOpenApiDocument(
            string openapi,
            FileInfo output,
            OpenApiSpecVersion?version,
            OpenApiFormat?format,
            LogLevel loglevel,
            bool inline,
            bool resolveexternal,
            string filterbyoperationids,
            string filterbytags,
            string filterbycollection
            )
        {
            var logger = ConfigureLoggerInstance(loglevel);

            try
            {
                if (string.IsNullOrEmpty(openapi))
                {
                    throw new ArgumentNullException(nameof(openapi));
                }
            }
            catch (ArgumentNullException ex)
            {
                logger.LogError(ex.Message);
                return;
            }
            try
            {
                if (output == null)
                {
                    throw new ArgumentException(nameof(output));
                }
            }
            catch (ArgumentException ex)
            {
                logger.LogError(ex.Message);
                return;
            }
            try
            {
                if (output.Exists)
                {
                    throw new IOException("The file you're writing to already exists. Please input a new file path.");
                }
            }
            catch (IOException ex)
            {
                logger.LogError(ex.Message);
                return;
            }

            var stream = await GetStream(openapi, logger);

            // Parsing OpenAPI file
            var stopwatch = new Stopwatch();

            stopwatch.Start();
            logger.LogTrace("Parsing OpenApi file");
            var result = new OpenApiStreamReader(new OpenApiReaderSettings
            {
                ReferenceResolution = resolveexternal ? ReferenceResolutionSetting.ResolveAllReferences : ReferenceResolutionSetting.ResolveLocalReferences,
                RuleSet             = ValidationRuleSet.GetDefaultRuleSet()
            }
                                                 ).ReadAsync(stream).GetAwaiter().GetResult();
            var document = result.OpenApiDocument;

            stopwatch.Stop();

            var context = result.OpenApiDiagnostic;

            if (context.Errors.Count > 0)
            {
                var errorReport = new StringBuilder();

                foreach (var error in context.Errors)
                {
                    errorReport.AppendLine(error.ToString());
                }
                logger.LogError($"{stopwatch.ElapsedMilliseconds}ms: OpenApi Parsing errors {string.Join(Environment.NewLine, context.Errors.Select(e => e.Message).ToArray())}");
            }
            else
            {
                logger.LogTrace("{timestamp}ms: Parsed OpenApi successfully. {count} paths found.", stopwatch.ElapsedMilliseconds, document.Paths.Count);
            }

            Func <string, OperationType?, OpenApiOperation, bool> predicate;

            // Check if filter options are provided, then slice the OpenAPI document
            if (!string.IsNullOrEmpty(filterbyoperationids) && !string.IsNullOrEmpty(filterbytags))
            {
                throw new InvalidOperationException("Cannot filter by operationIds and tags at the same time.");
            }
            if (!string.IsNullOrEmpty(filterbyoperationids))
            {
                logger.LogTrace("Creating predicate based on the operationIds supplied.");
                predicate = OpenApiFilterService.CreatePredicate(operationIds: filterbyoperationids);

                logger.LogTrace("Creating subset OpenApi document.");
                document = OpenApiFilterService.CreateFilteredDocument(document, predicate);
            }
            if (!string.IsNullOrEmpty(filterbytags))
            {
                logger.LogTrace("Creating predicate based on the tags supplied.");
                predicate = OpenApiFilterService.CreatePredicate(tags: filterbytags);

                logger.LogTrace("Creating subset OpenApi document.");
                document = OpenApiFilterService.CreateFilteredDocument(document, predicate);
            }
            if (!string.IsNullOrEmpty(filterbycollection))
            {
                var fileStream = await GetStream(filterbycollection, logger);

                var requestUrls = ParseJsonCollectionFile(fileStream, logger);

                logger.LogTrace("Creating predicate based on the paths and Http methods defined in the Postman collection.");
                predicate = OpenApiFilterService.CreatePredicate(requestUrls: requestUrls, source: document);

                logger.LogTrace("Creating subset OpenApi document.");
                document = OpenApiFilterService.CreateFilteredDocument(document, predicate);
            }

            logger.LogTrace("Creating a new file");
            using var outputStream = output?.Create();
            var textWriter = outputStream != null ? new StreamWriter(outputStream) : Console.Out;

            var settings = new OpenApiWriterSettings()
            {
                ReferenceInline = inline ? ReferenceInlineSetting.InlineLocalReferences : ReferenceInlineSetting.DoNotInlineReferences
            };

            var            openApiFormat  = format ?? GetOpenApiFormat(openapi, logger);
            var            openApiVersion = version ?? result.OpenApiDiagnostic.SpecificationVersion;
            IOpenApiWriter writer         = openApiFormat switch
            {
                OpenApiFormat.Json => new OpenApiJsonWriter(textWriter, settings),
                OpenApiFormat.Yaml => new OpenApiYamlWriter(textWriter, settings),
                _ => throw new ArgumentException("Unknown format"),
            };

            logger.LogTrace("Serializing to OpenApi document using the provided spec version and writer");

            stopwatch.Start();
            document.Serialize(writer, openApiVersion);
            stopwatch.Stop();

            logger.LogTrace($"Finished serializing in {stopwatch.ElapsedMilliseconds}ms");

            textWriter.Flush();
        }
        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);
        }
Esempio n. 10
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);
        }