{"id":13629674,"url":"https://github.com/alekshura/SourceApi","last_synced_at":"2025-04-17T09:35:38.674Z","repository":{"id":124910499,"uuid":"420145950","full_name":"alekshura/SourceApi","owner":"alekshura","description":"API first Open API code generator based on json or yaml definitions. ","archived":false,"fork":false,"pushed_at":"2021-12-02T10:31:08.000Z","size":191,"stargazers_count":13,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-08-01T22:44:08.534Z","etag":null,"topics":["alekshura","dotnet-standard","openapi","openapi3","sourcegenerator"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alekshura.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-10-22T15:16:23.000Z","updated_at":"2024-06-05T00:24:53.000Z","dependencies_parsed_at":"2024-01-06T02:10:09.192Z","dependency_job_id":"c26643b8-0f31-4a75-bf5f-be7251e28e6b","html_url":"https://github.com/alekshura/SourceApi","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alekshura%2FSourceApi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alekshura%2FSourceApi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alekshura%2FSourceApi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alekshura%2FSourceApi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alekshura","download_url":"https://codeload.github.com/alekshura/SourceApi/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223751357,"owners_count":17196620,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alekshura","dotnet-standard","openapi","openapi3","sourcegenerator"],"created_at":"2024-08-01T22:01:16.248Z","updated_at":"2024-11-08T20:31:39.421Z","avatar_url":"https://github.com/alekshura.png","language":"C#","readme":"# \u003cimg src=\"/Compentio.Assets/Logo.png\" align=\"left\" width=\"50\"\u003e SourceApi\n\n[![NuGet](http://img.shields.io/nuget/v/Compentio.SourceApi.svg)](https://www.nuget.org/packages/Compentio.SourceApi)\n![Nuget](https://img.shields.io/nuget/dt/Compentio.SourceApi)\n![GitHub](https://img.shields.io/github/license/alekshura/SourceApi)\n![GitHub top language](https://img.shields.io/github/languages/top/alekshura/SourceApi)\n\n# Introduction\nTwo different approaches for Web API implementation often used by development teams: `code first` and `API first`.\nDuring the `code first` approach Web API Controllers are implemented, Swagger/Open API libraries are added to the application and finnaly it deployed.\nOpen API libraries actually adds a middlewares to application which serve Open API definitions, \nSwagger UI thus the clients can use this definitions to generate code or use test API using UI.    \n\nIn second, `API First` approach, API defined in `json` or `yaml` files using [Open API standard](https://swagger.io/specification/) first and after that\nserver or client code, interfaces or DTO's are generated in application.\n\nThis approach is technology agnostic: API can be disigned independently from technologies used in cloud native architecture with a wide tech stack, then \nshared between defferent teams (`Java`, `.NET`, `NodeJS`, `Frontend`, ect.) and ech team can decide about what has to be done with it: to generate only DTO's, or \ncreate base abstract controllers, with routes, documentation and DTO's or to generate client code for consume the API.\n\n`SourceApi` is a code generator that helps to use `API First` approach for .NET Core 3+ (also .NET 5+) Web API applications:\nduring design time in Visual Studio IDE it generates abstract base Controllers classes and DTO's that can be used by developer to implement the target functionality.\n\nIt is based on [Source Generators Additional File Transaformation](https://github.com/dotnet/roslyn/blob/main/docs/features/source-generators.cookbook.md#additional-file-transformation) feature\nwhere it is possible to be able to transform an external non-C# file into an equivalent C# representation.\n\nRico Suter's [NSwag](https://github.com/RicoSuter/NSwag) is used underhood:\n- [CSharpControllerGenerator](https://github.com/RicoSuter/NSwag/wiki/CSharpControllerGenerator) is used for abstract base controllers code generation\n- [CSharpClientGenerator](https://github.com/RicoSuter/NSwag/wiki/CSharpClientGenerator) used for DTO's only code generation mode. \n\n# Installation\nInstall using nuget package manager:\n\n```console\nInstall-Package Compentio.SourceApi\n```\n\nor `.NET CLI`:\n\n```console\ndotnet add package Compentio.SourceApi\n```\n\n# How to use\nIn basic and most simple scenario: add Open API definition file or files (`*.json` and `*.yaml` formats are supported) to you project as `AdditionalFiles`:\n\n\u003e```xml\n\u003e\u003cItemGroup\u003e\n\u003e   \u003cAdditionalFiles Include=\"OpenApi/Pets.json\"\u003e\n\u003e     \u003cCopyToOutputDirectory\u003ePreserveNewest\u003c/CopyToOutputDirectory\u003e\n\u003e   \u003c/AdditionalFiles\u003e\n\u003e   \u003cAdditionalFiles Include=\"OpenApi/Users.yaml\"\u003e\n\u003e     \u003cCopyToOutputDirectory\u003ePreserveNewest\u003c/CopyToOutputDirectory\u003e\n\u003e   \u003c/AdditionalFiles\u003e\n\u003e \u003c/ItemGroup\u003e \n\nor in file properties in Visual Studio:\n\n\u003cimg src=\"/Compentio.Assets/AdditionalFile.png\" align=\"center\" width=\"95%\"\u003e\n\nYou will see generated abstract controllers in \n\u003e Dependencies -\u003e Analyzers -\u003e Compentio.SourceApi \u003e Compentio.SourceApi.Generator.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"/Compentio.Assets/GeneratedFiles.png\" align=\"center\" width=\"50%\"\u003e\n\u003c/p\u003e\n\n\u003e! For now use one Open API file per controller. [Open API Tags](https://swagger.io/docs/specification/grouping-operations-with-tags/) are not supported.\n\nNow you can use these base controllers during implementation of you Web API. The DTO's are generated, routes are defined, documentation for the API methods also used from \nOpen API definitions:\n\n```cs\n[ApiController]\n[ApiConventionType(typeof(DefaultApiConventions))]\npublic class StoreController : StoreControllerBase\n{\n\t/// \u003cinheritdoc /\u003e\n\tpublic override Task\u003cIActionResult\u003e DeleteOrder([BindRequired] long orderId)\n\t{\n\t\tthrow new NotImplementedException();\n\t}\n\n\t/// \u003cinheritdoc /\u003e\n\tpublic override Task\u003cActionResult\u003cIDictionary\u003cstring, int\u003e\u003e\u003e GetInventory()\n\t{\n\t\tthrow new NotImplementedException();\n\t}\n\n\t/// \u003cinheritdoc /\u003e\n\tpublic override Task\u003cActionResult\u003cOrder\u003e\u003e GetOrderById([BindRequired] long orderId)\n\t{\n\t\tthrow new NotImplementedException();\n\t}\n\n\t/// \u003cinheritdoc /\u003e\n\tpublic override Task\u003cActionResult\u003cOrder\u003e\u003e PlaceOrder([BindRequired, FromBody] Order body)\n\t{\n\t\tthrow new NotImplementedException();\n\t}\n}\n```\n\n\u003e! You need to add `\u003cinheritdoc /\u003e` tag to show documentation from base class in Swagger.\n\nAnd example of generated code for base controller class:\n\n```cs\n\n//----------------------\n// \u003cauto-generated\u003e\n//     Generated using the NSwag toolchain v13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v12.0.0.2)) (http://NSwag.org)\n// \u003c/auto-generated\u003e\n//----------------------\n#pragma warning disable 108 // Disable \"CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended.\"\n\n#pragma warning disable 114 // Disable \"CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.\"\n\n#pragma warning disable 472 // Disable \"CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?'\n\n#pragma warning disable 1573 // Disable \"CS1573 Parameter '...' has no matching param tag in the XML comment for ...\n\n#pragma warning disable 1591 // Disable \"CS1591 Missing XML comment for publicly visible type or member ...\"\n\n#pragma warning disable 8073 // Disable \"CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'\"\n\n#pragma warning disable 3016 // Disable \"CS3016 Arrays as attribute arguments is not CLS-compliant\"\n\nnamespace Compentio.SourceApi.WebExample.Controllers\n{\n    using System = global::System;\n\n    [System.CodeDom.Compiler.GeneratedCode(\"NSwag\", \"13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v12.0.0.2))\")]\n    [Microsoft.AspNetCore.Mvc.Route(\"api/v1\")]\n    public abstract class StoreControllerBase : Microsoft.AspNetCore.Mvc.ControllerBase\n    {\n        /// \u003csummary\u003eReturns pet inventories by status\u003c/summary\u003e\n        /// \u003creturns\u003esuccessful operation\u003c/returns\u003e\n        [Microsoft.AspNetCore.Mvc.HttpGet, Microsoft.AspNetCore.Mvc.Route(\"store/inventory\")]\n        public abstract System.Threading.Tasks.Task\u003cMicrosoft.AspNetCore.Mvc.ActionResult\u003cSystem.Collections.Generic.IDictionary\u003cstring, int\u003e\u003e\u003e GetInventory();\n        /// \u003csummary\u003ePlace an order for a pet\u003c/summary\u003e\n        /// \u003cparam name = \"body\"\u003eorder placed for purchasing the pet\u003c/param\u003e\n        /// \u003creturns\u003esuccessful operation\u003c/returns\u003e\n        [Microsoft.AspNetCore.Mvc.HttpPost, Microsoft.AspNetCore.Mvc.Route(\"store/order\")]\n        public abstract System.Threading.Tasks.Task\u003cMicrosoft.AspNetCore.Mvc.ActionResult\u003cOrder\u003e\u003e PlaceOrder([Microsoft.AspNetCore.Mvc.FromBody][Microsoft.AspNetCore.Mvc.ModelBinding.BindRequired] Order body);\n        /// \u003csummary\u003eFind purchase order by ID\u003c/summary\u003e\n        /// \u003cparam name = \"orderId\"\u003eID of pet that needs to be fetched\u003c/param\u003e\n        /// \u003creturns\u003esuccessful operation\u003c/returns\u003e\n        [Microsoft.AspNetCore.Mvc.HttpGet, Microsoft.AspNetCore.Mvc.Route(\"store/order/{orderId}\")]\n        public abstract System.Threading.Tasks.Task\u003cMicrosoft.AspNetCore.Mvc.ActionResult\u003cOrder\u003e\u003e GetOrderById([Microsoft.AspNetCore.Mvc.ModelBinding.BindRequired] long orderId);\n        /// \u003csummary\u003eDelete purchase order by ID\u003c/summary\u003e\n        /// \u003cparam name = \"orderId\"\u003eID of the order that needs to be deleted\u003c/param\u003e\n        [Microsoft.AspNetCore.Mvc.HttpDelete, Microsoft.AspNetCore.Mvc.Route(\"store/order/{orderId}\")]\n        public abstract System.Threading.Tasks.Task\u003cMicrosoft.AspNetCore.Mvc.IActionResult\u003e DeleteOrder([Microsoft.AspNetCore.Mvc.ModelBinding.BindRequired] long orderId);\n    }\n\n    [System.CodeDom.Compiler.GeneratedCode(\"NJsonSchema\", \"10.5.2.0 (Newtonsoft.Json v12.0.0.2)\")]\n    public partial class Order\n    {\n        [Newtonsoft.Json.JsonProperty(\"id\", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]\n        public long Id { get; set; }\n\n        [Newtonsoft.Json.JsonProperty(\"petId\", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]\n        public long PetId { get; set; }\n\n        [Newtonsoft.Json.JsonProperty(\"quantity\", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]\n        public int Quantity { get; set; }\n\n        [Newtonsoft.Json.JsonProperty(\"shipDate\", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]\n        public System.DateTimeOffset ShipDate { get; set; }\n\n        /// \u003csummary\u003eOrder Status\u003c/summary\u003e\n        [Newtonsoft.Json.JsonProperty(\"status\", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]\n        [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]\n        public OrderStatus Status { get; set; }\n\n        [Newtonsoft.Json.JsonProperty(\"complete\", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]\n        public bool Complete { get; set; } = false;\n    }\n\n    [System.CodeDom.Compiler.GeneratedCode(\"NJsonSchema\", \"10.5.2.0 (Newtonsoft.Json v12.0.0.2)\")]\n    public enum OrderStatus\n    {\n        [System.Runtime.Serialization.EnumMember(Value = @\"placed\")]\n        Placed = 0,\n        [System.Runtime.Serialization.EnumMember(Value = @\"approved\")]\n        Approved = 1,\n        [System.Runtime.Serialization.EnumMember(Value = @\"delivered\")]\n        Delivered = 2,\n    }\n}\n#pragma warning restore 1591\n#pragma warning restore 1573\n#pragma warning restore 472\n#pragma warning restore 114\n#pragma warning restore 108\n#pragma warning restore 3016\n\n```\n\u003e The default namespace here is concatenation of you project name and directory of Open API definitions.\n\u003e To change namespace see `Configuration`   \n\n# Configuration\n\nTo customize the generated code and override defaults `SourceApi` \n[consumes MSBuild properties and metadata](https://github.com/dotnet/roslyn/blob/main/docs/features/source-generators.cookbook.md#consume-msbuild-properties-and-metadata).\n\nTwo configuration properies you can dafine in `*.cproj` file to customize SourceApi generator:\n- SourceApi_GeneratorNamespace - you can define namespace for generated classes\n- SourceApi_GenerateOnlyContracts - you can generate only DTO's without Controller base classes.\n\n\u003eIn a case if properties are not added, default values are used: `SourceApi_GenerateOnlyContracts` is set to `False` and for `SourceApi_GeneratorNamespace` \n\u003econcatenation of you project name and directory of Open API definitions is used\n\nFor global configuration (used for all added Open API files) define these parameters in `\u003cPropertyGroup\u003e section:\n\n```xml\n\u003cPropertyGroup\u003e\n   \u003cTargetFramework\u003enet5.0\u003c/TargetFramework\u003e\n   \u003cGenerateDocumentationFile\u003etrue\u003c/GenerateDocumentationFile\u003e\n   \u003cNoWarn\u003e$(NoWarn);1591\u003c/NoWarn\u003e\n   ...\n   \u003cSourceApi_GeneratorNamespace\u003eCompentio.SourceApi.WebExample.Controllers\u003c/SourceApi_GeneratorNamespace\u003e\n   \u003cSourceApi_GenerateOnlyContracts\u003efalse\u003c/SourceApi_GenerateOnlyContracts\u003e\n\u003c/PropertyGroup\u003e\n ```\n\n\nYou can also define these parameters per file in `\u003cItemGroup\u003e`: \n\n```xml\n\u003cItemGroup\u003e\n   \u003cAdditionalFiles Include=\"OpenApi\\Pets.yaml\"/\u003e\n   \u003cAdditionalFiles Include=\"OpenApi\\Store.yaml\" SourceApi_GeneratorNamespace=\"Compentio.SourceApi.WebExample.WebApi\"/\u003e\n   \u003cAdditionalFiles Include=\"OpenApi\\Users.yaml\" SourceApi_GeneratorNamespace=\"Compentio.SourceApi.WebExample.WebApi\" SourceApi_GenerateOnlyContracts = \"true\"/\u003e\n \u003c/ItemGroup\u003e\n \u003cImport Project=\"..\\Compentio.SourceApi\\Generator.props\" /\u003e\n```\n\nHere in a case global configuration exists, for `Pets.yaml` global config is used, for `Store.yaml` namespace is overriden and for `Users.yaml` \n`SourceApi_GeneratorNamespace` and `SourceApi_GenerateOnlyContracts` are overriden.\n\n","funding_links":[],"categories":["Do not want to test 112 ( old ISourceGenerator )","Source Generators"],"sub_categories":["1. [ThisAssembly](https://ignatandrei.github.io/RSCG_Examples/v2/docs/ThisAssembly) , in the [EnhancementProject](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#enhancementproject) category","Webprogramming"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falekshura%2FSourceApi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falekshura%2FSourceApi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falekshura%2FSourceApi/lists"}