Git Product home page Git Product logo

refitter's Issues

Duplicate Accept Headers

Description
Duplicate accept headers like [Headers("Accept: application/json, application/json")] are generated when an endpoint has multiple responses with a payload. In this case, an endpoint can respond with HTTP 200 or 203 and both responses include a application/json payload in the response body

Support Key: ptbddgt

OpenAPI Specifications

openapi: '3.0.0'
info:
  version: 'v1'
  title: 'Test API'
servers:
  - url: 'https://test.host.com/api/v1'
paths:
  /jobs/{job-id}:
    get:
      tags:
      - 'Jobs'
      summary: 'Get job details'
      description: 'Get the details of the specified job.'
      parameters:
        - in: 'path'
          name: 'job-id'
          description: 'Job ID'
          required: true
          schema:
            type: 'string'
      responses:
        '200':
          description: 'successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobResponse'
        '203':
          description: 'successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobResponse'
components:
  schemas:
    JobResponse:
      type: 'object'
      properties:
        job:
          type: 'object'
          properties:
            start-date:
              type: 'string'
              format: 'date-time'
            details:
              type: 'string'

Generated code:

public interface ITestAPI
{
    /// <summary>
    /// Get the details of the specified job.
    /// </summary>
    [Headers("Accept: application/json, application/json")]
    [Get("/jobs/{job-id}")]
    Task<JobResponse> Jobs([AliasAs("job-id")] string job_id);
}

Refitter fails to generate FormData parameter for file upload

Describe the bug

Refitter ignores the IFormFile parameter decorated with [FromForm] which is used for receiving file upload.
The controller parameter is completely missed in the generated refit interface method.

If it worked, the generated method would have [Multipart] as method annotation and StreamPart as the type for IFormFile parameter
https://github.com/reactiveui/refit#multipart-uploads

Support Key: 8hz0dgn

OpenAPI Specifications
"requestBody": {
"content": {
"multipart/form-data": {
"schema": {
"type": "object",
"properties": {
"formFile": {
"type": "string",
"format": "binary"
}
}
},
"encoding": {
"formFile": {
"style": "form"
}
}
}
}
}

Additional context

Downloading OpenAPI specification from URI using `content-encoding: gzip` fails

Describe the bug
Attempting to download https://developers.intellihr.io/docs/v1/swagger.json results in garbled/binary JSON file being saved.
The file is publicly accessible and does not require auth, so it should be reproducible. I have a similar bug reported to another of my projects

Support Key: ptbddgt

OpenAPI Specifications
https://developers.intellihr.io/docs/v1/swagger.json

Additional context
After making a cURL request to the URI, I found out that that Refitter doesn't support URI's returning content-encoding: gzip

The following command:

curl https://developers.intellihr.io/docs/v1/swagger.json -v

returns:

*   Trying 18.173.5.96:443...
* Connected to developers.intellihr.io (18.173.5.96) port 443 (#0)
* schannel: disabled automatic use of client certificate
* ALPN: offers http/1.1
* ALPN: server accepted http/1.1
* using HTTP/1.1
> GET /docs/v1/swagger.json HTTP/1.1
> Host: developers.intellihr.io
> User-Agent: curl/8.0.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: application/json
< Content-Length: 70281
< Connection: keep-alive
< x-amz-id-2: KbRD1cgNtvruOBsX/Xyxmvcnca3PLvJnb/o2tjFQV9jEBFB8rGareRNv8mj6ub1jd5AX6to/3Y4=
< x-amz-request-id: 898QGBVBFHSY8SNM
< Date: Mon, 04 Sep 2023 09:05:13 GMT
< Last-Modified: Mon, 04 Sep 2023 00:29:47 GMT
< ETag: "614dd1541549b50f0b008444459f94da"
< x-amz-server-side-encryption: AES256
< Content-Encoding: gzip
< x-amz-version-id: SDHILeEo0afGzS0a0a5ljWAToLzlnjWQ
< Accept-Ranges: bytes
< Server: AmazonS3
< Strict-Transport-Security: max-age=15552000; includeSubDomains; preload
< X-Cache: Miss from cloudfront
< Via: 1.1 78a128491ada170a2d9b82ba12d23d7a.cloudfront.net (CloudFront)
< X-Amz-Cf-Pop: CPH50-P1
< X-Amz-Cf-Id: pxEry-q7wwDbWme5LptPbbbMHcKPttbcKbFp4V5LmAIQBOtKNkvMIw==
<
Warning: Binary output can mess up your terminal. Use "--output -" to tell
Warning: curl to output it to your terminal anyway, or consider "--output
Warning: <FILE>" to save to a file.
* Failure writing output to destination
* Closing connection 0
* schannel: shutting down SSL/TLS connection with developers.intellihr.io port 443

Please add support for kebab string casing parameters

I found Refitter didn't generate valid C# output with kebab string casing parameters.
See job-id in this sample OpenAPI 3.0 file:

openapi: "3.0.0"
info:
  version: "v1"
  title: "Test API"
servers:
  - url: "https://test.host.com/api/v1"
paths:
  /jobs/{job-id}:
    get:
      tags:
      - "Jobs"
      summary: "Get job details"
      description: "Get the details of the specified job."
      parameters:
        - in: "path"
          name: "job-id"
          description: "Job ID"
          required: true
          schema:
            type: "string"
      responses:
        "200":
          description: "successful operation"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobResponse"
components:
  schemas:
    JobResponse:
      type: "object"
      properties:
        job:
          type: "object"
          properties:
            start-date:
              type: "string"
              format: "date-time"
            details:
              type: "string"

Invalid C# parameter output snippet:

        [Get("/jobs/{job-id}")]
        Task<JobResponse> Jobs(string job-id);

Support $ref references to separate files in OpenAPI specifications.

Is your feature request related to a problem? Please describe.
I'd like to generate the refit internfaces for an OpenAPI specification that consists of multiple files that are linked to each other using $ref (see https://swagger.io/docs/specification/using-ref/).

Describe the solution you'd like
It would be great if refitter would support $ref.

Describe alternatives you've considered
The alternative would be to merge the files first using a different tool. But it would be much easier, if this extra step could be avoided.

Additional context
Thank you for your help.

Generated output has Task return type instead of expected Task<T>

Hi,

I have noticed the following with refitter 0.5.26. With OpenApi spec input:

openapi: 3.0.0
x-stoplight:
  id: w776sltk0h1bo
info:
  title: Gamenight
  version: '1.0'
  contact:
    name: Dennis Brentjes
    email: [email protected]
    url: 'https://brentj.es'
  description: Api specifaction for a Gamenight server
  license:
    name: MIT
servers:
  - url: 'http://localhost:8080'
    description: Gamenight
paths:
  /token:
    get:
      summary: ''
      operationId: get-token
      responses:
        '200':
          $ref: '#/components/responses/TokenResponse'
      requestBody:
        $ref: '#/components/requestBodies/LoginRequest'
      description: Submit your credentials to get a JWT-token to use with the rest of the api.
      parameters: []
components:
  schemas:
    Token:
      title: Token
      x-stoplight:
        id: 8pz19kigm1jer
      type: object
      properties:
        jwt_token:
          type: string
    Login:
      title: Login
      type: object
      properties:
        username:
          type: string
        password:
          type: string
      required:
        - username
        - password
  requestBodies:
    LoginRequest:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Login'
  responses:
    TokenResponse:
      description: Example response
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Token'

I get the following output, with Task missing their response output as Task result.

// <auto-generated>
//     This code was generated by Refitter.
// </auto-generated>

using Refit;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace GeneratedCode
{
    public interface IGamenight
    {

        /// <summary>
        /// Submit your credentials to get a JWT-token to use with the rest of the api.
        /// </summary>
        [Get("/token")]
        Task GetToken([Body] object body);

    }


}


//----------------------
// <auto-generated>
//     Generated using the NSwag toolchain v13.19.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v10.0.0.0)) (http://NSwag.org)
// </auto-generated>
//----------------------

#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended."
#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."
#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?'
#pragma warning disable 612 // Disable "CS0612 '...' is obsolete"
#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ...
#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..."
#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?'"
#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant"
#pragma warning disable 8603 // Disable "CS8603 Possible null reference return"

namespace GeneratedCode
{
    using System = global::System;

    

    [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "13.19.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v10.0.0.0))")]
    public partial class Token
    {

        [System.Text.Json.Serialization.JsonPropertyName("jwt_token")]

        [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault)]   
        public string Jwt_token { get; set; }

        private System.Collections.Generic.IDictionary<string, object> _additionalProperties;

        [System.Text.Json.Serialization.JsonExtensionData]
        public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
        {
            get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary<string, object>()); }
            set { _additionalProperties = value; }
        }

    }

    [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "13.19.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v10.0.0.0))")]
    public partial class Login
    {

        [System.Text.Json.Serialization.JsonPropertyName("username")]

        [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.Never)]   
        [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
        public string Username { get; set; }

        [System.Text.Json.Serialization.JsonPropertyName("password")]

        [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.Never)]   
        [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
        public string Password { get; set; }

        private System.Collections.Generic.IDictionary<string, object> _additionalProperties;

        [System.Text.Json.Serialization.JsonExtensionData]
        public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
        {
            get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary<string, object>()); }
            set { _additionalProperties = value; }
        }

    }


}

#pragma warning restore  108
#pragma warning restore  114
#pragma warning restore  472
#pragma warning restore  612
#pragma warning restore 1573
#pragma warning restore 1591
#pragma warning restore 8073
#pragma warning restore 3016
#pragma warning restore 8603

When I apply this patch to refitter

diff --git a/src/Refitter.Core/RefitInterfaceGenerator.cs b/src/Refitter.Core/RefitInterfaceGenerator.cs
index ad588c4..0b19ab0 100644
--- a/src/Refitter.Core/RefitInterfaceGenerator.cs
+++ b/src/Refitter.Core/RefitInterfaceGenerator.cs
@@ -40,7 +40,7 @@ public class RefitInterfaceGenerator
                 var operation = operations.Value;
 
                 var returnTypeParameter = operation.Responses.ContainsKey("200")
-                    ? generator.GetTypeName(operation.Responses["200"].Schema, true, null)
+                    ? generator.GetTypeName(operation.Responses["200"].ActualResponse.Schema, true, null)
                     : null;
 
                 var returnType = GetReturnType(returnTypeParameter);

I get the expected output with Task returning their output.

// <auto-generated>
//     This code was generated by Refitter.
// </auto-generated>

using Refit;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace GeneratedCode
{
    public interface IGamenight
    {

        /// <summary>
        /// Submit your credentials to get a JWT-token to use with the rest of the api.
        /// </summary>
        [Get("/token")]
        Task<Token> GetToken([Body] object body);

    }


}


//----------------------
// <auto-generated>
//     Generated using the NSwag toolchain v13.19.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v10.0.0.0)) (http://NSwag.org)
// </auto-generated>
//----------------------

#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended."
#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."
#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?'
#pragma warning disable 612 // Disable "CS0612 '...' is obsolete"
#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ...
#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..."
#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?'"
#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant"
#pragma warning disable 8603 // Disable "CS8603 Possible null reference return"

namespace GeneratedCode
{
    using System = global::System;

    

    [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "13.19.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v10.0.0.0))")]
    public partial class Token
    {

        [System.Text.Json.Serialization.JsonPropertyName("jwt_token")]

        [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault)]   
        public string Jwt_token { get; set; }

        private System.Collections.Generic.IDictionary<string, object> _additionalProperties;

        [System.Text.Json.Serialization.JsonExtensionData]
        public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
        {
            get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary<string, object>()); }
            set { _additionalProperties = value; }
        }

    }

    [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "13.19.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v10.0.0.0))")]
    public partial class Login
    {

        [System.Text.Json.Serialization.JsonPropertyName("username")]

        [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.Never)]   
        [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
        public string Username { get; set; }

        [System.Text.Json.Serialization.JsonPropertyName("password")]

        [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.Never)]   
        [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
        public string Password { get; set; }

        private System.Collections.Generic.IDictionary<string, object> _additionalProperties;

        [System.Text.Json.Serialization.JsonExtensionData]
        public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
        {
            get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary<string, object>()); }
            set { _additionalProperties = value; }
        }

    }


}

#pragma warning restore  108
#pragma warning restore  114
#pragma warning restore  472
#pragma warning restore  612
#pragma warning restore 1573
#pragma warning restore 1591
#pragma warning restore 8073
#pragma warning restore 3016
#pragma warning restore 8603

The original behaviour seems to be a bug, I have no idea however if my "fix" has any impact on existing projects but I suspect that ActualResponse wil link be a reference to itself when not being used by a ref. But that is a question you might be able to answer.

Single output

Why using CLI (reading settings from .reffiter file) I get two outputs?
One in outputFolder specified in settings file, but with name from that file name (i.e. test.refitter -> test.g.cs) and another one named Ouput.cs in directory from which I ran the command?

Please, add outputFileName to .reffiter settings so that only one output is produced.

Generated nullable query method params are not set to a default value of null

I have a swagger doc with nullable query params and the generated Refit method signature has the param marked as nullable but not set with null by default so in order to call the method all of the nullable params need to be set as null vs just setting the params you want to set.

"/v1/accounts/{accountId}/addresses": { "get": { "tags": [ "AccountAddresses" ], "operationId": "AccountAddresses_GetAccountAddresses", "parameters": [ { "name": "accountId", "in": "path", "required": true, "schema": { "type": "integer", "format": "int64" }, "x-position": 1 }, { "name": "IsPrimary", "in": "query", "schema": { "type": "boolean", "nullable": true, "default": null } }, { "name": "IsActive", "in": "query", "schema": { "type": "boolean", "nullable": true } }, { "name": "IncludeSpouse", "in": "query", "schema": { "type": "boolean" } }, { "name": "SortBy", "in": "query", "style": "form", "explode": true, "schema": { "type": "array", "nullable": true, "xml": { "name": "ArrayOfstring", "wrapped": true }, "items": { "type": "string", "xml": { "name": "string" } } } }, { "name": "Search", "in": "query", "schema": { "type": "string", "nullable": true } }, { "name": "PageNumber", "in": "query", "schema": { "type": "integer", "format": "int32" } }, { "name": "PageSize", "in": "query", "schema": { "type": "integer", "format": "int32" } }, { "name": "ExportFileName", "in": "query", "schema": { "type": "string", "nullable": true } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PagedApiResponseOfIEnumerableOfAccountAddressSummary" } } } } } }, "post": { "tags": [ "AccountAddresses" ], "operationId": "AccountAddresses_CreateAccountAddress", "parameters": [ { "name": "accountId", "in": "path", "required": true, "schema": { "type": "integer", "format": "int64" }, "x-position": 1 } ], "requestBody": { "x-name": "command", "content": { "application/json": { "schema": { "nullable": true, "$ref": "#/components/schemas/CreateAccountAddress" } } }, "x-position": 2 }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccountAddressSummary" } } } } } } },

Error on build - dependency to Microsoft.Bcl.AsyncInterfaces

REFITTER000 System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.Bcl.AsyncInterfaces, Version=7.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. Systém nemůže nalézt uvedený soubor.

I tried to install package Microsoft.Bcl.AsyncInterfaces, but it did not help. VS 2022, MAUI app project.
Thanks for help

Add support for generating IApiResponse<T> as return types

Add support for generating interfaces that returns Task<IApiResponse<T>> as the return type

Like this:

[Get("/pet/{petId}")]
Task<IApiResponse<Pet>> GetPetById(long petId);

So that if the call to GetPetById() fails then we can handle the negative response ourselves instead of catching an ApiException

var client = RestService.For<ISwaggerPetstore>("https://petstore3.swagger.io/api/v3");
var response = await client.GetPetById(2);

if (!response. IsSuccessStatusCode)
{
    // Do something about the failed response
}

var pet = response.Content;

The IApiResponse<T> interface comes from Refit and is defined as

/// <inheritdoc/>
public interface IApiResponse<out T> : IApiResponse
{
    /// <summary>
    /// Deserialized request content as <typeparamref name="T"/>.
    /// </summary>
    T? Content { get; }
}

/// <summary>
/// Base interface used to represent an API response.
/// </summary>
public interface IApiResponse : IDisposable
{
    /// <summary>
    /// HTTP response headers.
    /// </summary>
    HttpResponseHeaders Headers { get; }

    /// <summary>
    /// HTTP response content headers as defined in RFC 2616.
    /// </summary>
    HttpContentHeaders? ContentHeaders { get; }

    /// <summary>
    /// Indicates whether the request was successful.
    /// </summary>
    bool IsSuccessStatusCode { get; }

    /// <summary>
    /// HTTP response status code.
    /// </summary>
    HttpStatusCode StatusCode { get; }

    /// <summary>
    /// The reason phrase which typically is sent by the server together with the status code.
    /// </summary>
    string? ReasonPhrase { get; }

    /// <summary>
    /// The HTTP Request message which led to this response.
    /// </summary>
    HttpRequestMessage? RequestMessage { get; }

    /// <summary>
    /// HTTP Message version.
    /// </summary>
    Version Version { get; }

    /// <summary>
    /// The <see cref="ApiException"/> object in case of unsuccessful response.
    /// </summary>
    ApiException? Error { get; }
}

NSwag contracts

Maybe it's there and I couldn't find it, but when generating contracts, I'd like to specify some NSwag options like dateType (I don't like DateTimeOffset) or arrayType.

Generated files have inconsistent lined endings

Describe the bug
The generated files seem to have a mixture different lines endings. This dialog appears everytime a file is opened in VS.

image

Perhaps consider normalizing the line endings to whatever the OS environment the tools is run in :)

Support Key: kedfgny

OpenAPI Specifications
N/A

Additional context
Add any other context about the problem here.

Filter `--tag` broken

Describe the bug
Filtering a spec by tag using --tag A --tag B is currently broken as it only accepts the last filter entered.

Instead it should keep all paths that contain any of the tags.

Additional context

This is due to the following check constantly overriding previous results:
exclude = method.Value.Tags?.Exists(x => x == tag) != true;

The code may be rewritten, to remove the looping over includeTags by using the following:

var exclude = method.Value.Tags?.Exists(includeTags.Contains) != true;

Also, previously the enumerated collections were cloned, to not break the enumeration, after the rewrite the enumerators are reused (.ToArray() was removed)

7025d61#diff-275851d6772d6a0a03b9aaf63017c87331d0334e274deeebd199fe8cd07cc521R34

7025d61#diff-275851d6772d6a0a03b9aaf63017c87331d0334e274deeebd199fe8cd07cc521R37

deleting from an collection that is currently enumerated is undefined behavior

Generate Refit interfaces as `partial` by default

I propose ~~ a --partial Flag for the CLI.~~ refit interfaces to be generated as partial interfaces by default, which would comply with most other code-generator tools out there and the way the models are generated currently.

Reasoning being, for certain classes we need to add attributes, interfaces or utility methods which is currently not possible without modifying the generated code.

By having partial classes for Models and Refit-Interfaces, we can make use of a generated.additions.cs (example name) which contains the needed modifications.

// a.cs
internal partial interface ISomething
{

}

// a.additions.cs
[FancyAttribute("With Value")]
[Headers("X-App: MyApp")]
partial interface ISomething {}

Path to OpenAPI spec file is required in CLI command even when using a `--settings-file` parameter.

A new and a very convenient feature allows us to use refitter file via CLI with a --settings-file option in the following manner:
refitter ./openapi.json --settings-file ./openapi.refitter.

The path to OpenAPI spec file (./openapi.json) cannot be dropped from the CLI command as it results in Input file is required error.

At the same time refitter file has an openApiPath parameter with seemingly the same meaning. Thus it seems redundant and error-prone to have a mandatory path in a CLI command as it becomes unclear which of the paths would be used in the end - the one in CLI command or the one in .refitter file.

IServiceCollectionExtensions extra closing parenthesis with httpMessageHandlers

Describe the bug
Version 0.8.2 generates a new dependency injection IServiceCollectionExtensions, and seems to introduce a syntax error as shown below:

            services
                .AddRefitClient<IBackendApi>()
                .ConfigureHttpClient(c => c.BaseAddress = baseUrl)
                .AddHttpMessageHandler<AuthorizationMessageHandler>()
                .AddHttpMessageHandler<TelemetryMessageHandler>()); // extra ) here

This seems to originate from this line

I wasn't sure if there were use cases that this worked, but this is the config that does not work, and probably has something to do with the httpMessageHandlers in the dependency injection section.

{
    "openApiPath": "../../Services/BackendApi/wwwroot/swaggerfile.json",
    "namespace": "App.Services.Clients.BackendApiClient",
    "naming": {
      "useOpenApiTitle": false,
      "interfaceName": "BackendApiClient"
    },
    "generateContracts": true,
    "generateXmlDocCodeComments": true,
    "addAutoGeneratedHeader": false,
    "addAcceptHeaders": false,
    "returnIApiResponse": true,
    "generateOperationHeaders": true,
    "typeAccessibility": 0,
    "useCancellationTokens": false,
    "useIsoDateFormat": true,
    "multipleInterfaces": 2,
    "generateDeprecatedOperations": false,
    "operationNameTemplate": "{operationName}Async",
    "optionalParameters": true,
    "outputFolder": "./Clients",
    "includePathMatches": [
      "^(?!.*[Ii]ntegration[Ee]vent).*\/api.*"
     ],
    "dependencyInjectionSettings": {
      "httpMessageHandlers": [
          "AuthorizationMessageHandler",
          "TelemetryMessageHandler"
      ]
    }
  }

I cannot include the API spec for privacy reasons.

String parameters with format 'date' get no Format in the QueryAttribute

Describe the bug
When generating a client for an OpenApi (swagger version 2.0) with url (query) parameters of type string with a format of 'date' are generated as a regular DateTimeOffset without a 'Date' format.

Support Key: mmt1dt0

OpenAPI Specifications
(not the full specs, but this is the gist of it)

{
  "swagger": "2.0",
  "info": {
    "title": "XX",
    "version": "0.0.0"
  },
  "host": "x.io",
  "basePath": "/",
  "schemes": [
    "https"
  ],
  "paths": {
    "/t/dummy/{employee_id}": {
      "get": {
        "summary": "X",
        "description": "X",
        "operationId": "dummy",
        "parameters": [
          {
            "name": "employee_id",
            "in": "path",
            "description": "the specific employee",
            "required": true,
            "format": "int64",
            "type": "integer"
          },
          {
            "name": "valid_from",
            "in": "query",
            "description": "the start of the period",
            "required": true,
            "format": "date",
            "type": "string"
          },
          {
            "name": "valid_to",
            "in": "query",
            "description": "the end of the period",
            "required": true,
            "format": "date",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "No response was specified",
            "schema": {
              "$ref": "#/definitions/DummyList"
            }
          }
        },
        "produces": [
          "application/json",
          "application/xml"
        ]
      }
    },
  }
},

Additional context
Resulting code is:

        /// <summary>
        /// X
        /// </summary>
        [Get("/t/dummy/{employee_id}")]
        Task<DummyList> Dummy(long employee_id, [Query(CollectionFormat.Multi)] System.DateTimeOffset valid_from, [Query(CollectionFormat.Multi)] System.DateTimeOffset valid_to);

When executing, this results in errors.

Expected code:

        /// <summary>
        /// X
        /// </summary>
        [Get("/t/dummy/{employee_id}")]
        Task<DummyList> Dummy(long employee_id, [Query(CollectionFormat.Multi, Format = "yyyy-MM-dd")] System.DateTimeOffset valid_from, [Query(CollectionFormat.Multi, Format = "yyyy-MM-dd")] System.DateTimeOffset valid_to);

The same may be true for other date / time related formats, like 'time'. The same may also be true for entity classes where properties are generated as regular 'DateTimeOffset'.

Ideally, for 'time' and 'date', both the arguments of functions and the properties of entity classes would result in 'TimeOnly' and 'DateOnly', respectively, but I'm not sure if those are fully supported in Refit? I think they are for properties, not sure about function arguments.

Edit: apparently 'time' was no format in OpenApi (Swagger) v2, but 'date' apparently is.

The using of StringEnumConverter end up generating unserializable data when different NamingPolicy is needed

Describe the bug
This problem is with the same API as in #175, they use snake_lower_case for enum values and because JsonConverter will instantiate the JsonStringEnumConverter with default options the deserialization of any type with enum members will fail.

Registering a JsonStringEnumConverter in json serialization settings does not help, because as JsonConverter says: "When placed on a property or field, the specified converter will always be used.".

When JsonConverter is placed on the type, if you've a compatible serializer in settings that will be used.

I see multiple solutions here, but one of them is simple:

  • Have an option to NOT to emit [JsonConverter(typeof(JsonStringEnumConverter))] for enum properties. With this settings will have to be configured but gives great flexibility. Cons: if no settings configured Json serialization will fail because there is nothing registered for enums.
  • Move the JsonConverter from properties to the enum types.

Now:

public enum Foo
{
    FooOne = 0,
    FooTwo = 1
}

public class FooClass
{
    [JsonConverter(typeof(JsonStringEnumConverter))]
    public Foo enumValue { get; set; }
}

With fix:

[JsonConverter(typeof(JsonStringEnumConverter))]
public enum Foo
{
    FooOne = 0,
    FooTwo = 1
}

public class FooClass
{
    public Foo enumValue { get; set; }
}

This way there is no requirement to have settings configured for enums, but gives the flexibility to have the behavior changed or register even typed enum converters there.

Support Key: [my-support-key]
N/A

OpenAPI Specifications
Same as in #175

Additional context
[EnumMember] is not used with System.Text.Json so their declaration can be omitted.

Missing path parameters in parent

This was pointed out to me by @kgamecarter

Example YAML:

swagger: '2.0'
info:
  title: Reference parameters
  version: v0.0.1
paths:
  '/orders/{orderId}/order-items/{orderItemId}':
    parameters:
      - $ref: '#/parameters/OrderId'
      - $ref: '#/parameters/OrderItemId'
    delete:
      summary: Delete an order item
      description: >-
        This method allows to remove an order item from an order, by specifying
        their ids.
      responses:
        "204":
          description: No Content.
        default:
          description: Default response
          schema:
            $ref: '#/definitions/Error'
definitions:
  Error:
    type: object
    properties:
      message:
          type: string
parameters:
  OrderId:
    name: orderId
    in: path
    description: Identifier of the order.
    required: true
    type: string
    format: uuid
  OrderItemId:
    name: orderItemId
    in: path
    description: Identifier of the order item.
    required: true
    type: string
    format: uuid

Generated code:

public interface IReferenceparameters
{
	/// <summary>
	/// This method allows to remove an order item from an order, by specifying their ids.
	/// </summary>
	[Delete("/orders/{orderId}/order-items/{orderItemId}")]
	Task OrderItems();
}

missing orderId and orderItemId path parameters

Proposal: filter generated interfaces

This would allow us to only generate a subset of the interfaces/methods we need.

e.g. App might need "Configuration"-Controller/Group Methods, but not others.

by allowing things like the following, the consumers can specify exactly what they need.
matches are based on regex

--include '^/api/v1/user/.*'
--include '^/api/(?!pet\b)' // include everything that is NOT /api/pet

For a less fine grained, but also useful way, we could also add filter for the "Tags" section of an openapi spec.

matches are based value, case-insensitive

--include-tag User \
--include-tag Configuration

Allow for naming of methods when generating interfaces by endpoint

Is your feature request related to a problem?
I think it would be nice if it were possible to choose naming for methods when interfaces are generated ByEndpoint. Right now it is only possible to customize method names when all endpoints are in a single interface. When doing it for a single interface the {operationName} is a requirement, but I do not think this should be a requirement since there will only be one method in each interface.

Describe the solution you'd like
Example of how this could work:

{
    "openApiPath": "./openapi.json",
    "namespace": "Your.Namespace.Of.Choice.GeneratedCode",
    "multipleInterfaces": "ByEndpoint",
    "operationNameTemplate": "ExecuteAsync" // Default is Execute
}

The .refitter file above results in the following code:

/// <summary>
/// Update an existing pet
/// </summary>
public partial interface IUpdatePetEndpoint
{
    /// <summary>
    /// Update an existing pet by Id
    /// </summary>
    [Put("/pet")]
    Task<Pet> Execute([Body] Pet body);
}

/// <summary>
/// Add a new pet to the store
/// </summary>
public partial interface IAddPetEndpoint
{
    /// <summary>
    /// Add a new pet to the store
    /// </summary>
    [Post("/pet")]
    Task<Pet> Execute([Body] Pet body);
}

...

Allow specifying access modifiers for generated classes

First off, sorry for feature bombing

It would be very handy to be able to specify the access modifier for the generated DTOs and Clients.

backstory:

In our project, we have modularized everything and try to keep our public Api surface as small as possible.
We define most of our classes as internal unless they explicitly are needed in other places.

All HTTP calls a module needs and all their DTOs reside in the module and never leave it's scope.

Generated code doesn't build if operationId contains spaces

The generated code for OpenAPI specs where the operationId contains spaces, doesn't build

OpenAPI Specifications

openapi: '3.0.0'
paths:
  /jobs/{job-id}:
    get:
      tags:
      - 'Jobs'
      operationId: 'Get job details'
      description: 'Get the details of the specified job.'
      parameters:
        - in: 'path'
          name: 'job-id'
          description: 'Job ID'
          required: true
          schema:
            type: 'string'
      responses:
        '200':
          description: 'successful operation'

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Ignored or Blocked

These are blocked by an existing closed PR and will not be recreated unless you click a checkbox below.

Detected dependencies

devcontainer
.devcontainer/devcontainer.json
  • mcr.microsoft.com/devcontainers/dotnet 1-6.0
  • ghcr.io/devcontainers/features/powershell 1
  • ghcr.io/devcontainers/features/dotnet 2
github-actions
.github/workflows/build.yml
  • actions/checkout v4
  • actions/upload-artifact v3
.github/workflows/changelog.yml
  • actions/checkout v4
  • ruby/setup-ruby v1
.github/workflows/codecov.yml
  • actions/checkout v4
  • actions/upload-artifact v3
  • codecov/codecov-action v4
.github/workflows/docfx.yml
  • actions/checkout v4
  • peaceiris/actions-gh-pages v4
.github/workflows/release-template.yml
  • actions/checkout v4
  • nelonoel/branch-name v1.0.1
  • actions/upload-artifact v3
  • actions/github-script v7
.github/workflows/smoke-tests.yml
  • actions/checkout v4
.github/workflows/template-source-generator.yml
  • actions/checkout v4
.github/workflows/template-url.yml
  • actions/checkout v4
.github/workflows/template.yml
  • actions/checkout v4
  • actions/upload-artifact v3
nuget
src/Refitter.Core/Refitter.Core.csproj
  • OasReader 1.6.16.15
  • NSwag.Core.Yaml 14.1.0
  • NSwag.CodeGeneration.CSharp 14.1.0
  • Microsoft.SourceLink.GitHub 8.0.0
src/Refitter.SourceGenerator.Tests/Refitter.SourceGenerator.Tests.csproj
  • Polly.Contrib.WaitAndRetry 1.1.1
  • Microsoft.Extensions.Http.Polly 8.0.7
  • Refit.HttpClientFactory 7.1.2
  • coverlet.collector 6.0.2
  • xunit.runner.visualstudio 2.8.2
  • xunit 2.9.0
  • FluentAssertions 6.12.0
  • Microsoft.NET.Test.Sdk 17.10.0
  • Microsoft.Extensions.Http.Resilience 8.7.0
src/Refitter.SourceGenerator/Refitter.SourceGenerator.csproj
  • Refit 7.1.2
  • OasReader 1.6.16.15
  • NSwag.Core.Yaml 14.1.0
  • NSwag.CodeGeneration.CSharp 14.1.0
  • H.Generators.Extensions 1.22.0
  • Microsoft.CodeAnalysis.CSharp 4.8.0
src/Refitter.Tests/Refitter.Tests.csproj
  • coverlet.collector 6.0.2
  • xunit.runner.visualstudio 2.8.2
  • xunit 2.9.0
  • Microsoft.NET.Test.Sdk 17.10.0
  • Atc.Test 1.0.89
src/Refitter/Refitter.csproj
  • Microsoft.SourceLink.GitHub 8.0.0
  • Spectre.Console.Cli 0.49.1
  • Exceptionless 6.0.4

  • Check this box to trigger a request for Renovate to run again on this repository

Missing "Accept" Request Header in generated files based on OAS 3.0

When Refitter generates Refit interfaces from a OAS 3.0 spec file, it doesn't add the "Accept" header , which necesssary for ASP.NET Core's ProblemDetails Middleware to function properly.

According to OAS 3.0 specification, The content property in responses contains the MIME type expected in the response.

I'm suggesting to add an optional parameter addAcceptHeaders to add the accept header.
Examples

  • without addAcceptHeaders:
        /// Multiple status values can be provided with comma separated strings
        /// </summary>
        [Get("/pet/findByStatus")]
        Task<ICollection<Pet>> FindPetsByStatus([Query(CollectionFormat.Multi)] IEnumerable<Anonymous> status);
  • with addAcceptHeaders:
        /// Multiple status values can be provided with comma separated strings
        /// </summary>
        [Headers("Accept: application/json")]
        [Get("/pet/findByStatus")]
        Task<ICollection<Pet>> FindPetsByStatus([Query(CollectionFormat.Multi)] IEnumerable<Anonymous> status);```


Generated Method names contains invalid characters.

Describe the bug
Hello there, interesting project so gave it a go :)

The following command refitter https://api.hubspot.com/api-catalog-public/v1/apis/events/v3/send --namespace HubSpot --output ./GeneratedCode.cs generated this code

namespace HubSpot
{
    public interface ICustomBehavioralEventsAPI
    {

        /// <summary>
        /// Endpoint to send an instance of a behavioral event
        /// </summary>
        [Post("/events/v3/send")]
        Task Post/events/v3/send([Body] BehavioralEventHttpCompletionRequest body);

    }


}

As you can see the method name is not valid C#.

Support Key: kedfgny

OpenAPI Specifications
https://api.hubspot.com/api-catalog-public/v1/apis/events/v3/send

Additional context
Add any other context about the problem here.

Multipart endpoint [FromForm] decorated argument is missing from signature

Describe the bug
I have a receiving API endpoint with following signature:

[HttpPost("[action]/{bucketName}/{directory}", Name = nameof(UploadFileAsync))]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> UploadFileAsync(string bucketName,
	[FromRoute] string directory,
	[FromForm] UploadFileArgs uploadFileArgs,
	IFormFile file,
	CancellationToken cancellationToken)

using refitter CLI i get the following contract generated for the controller:

 /// <summary>
 /// Returns information about file
 /// </summary>
 [System.CodeDom.Compiler.GeneratedCode("Refitter", "0.7.5.0")]
 public interface IFileApi
 {
     [Headers("Accept: application/json")]
     [Get("/v1/File/GetFileInfo/{bucketName}/{objectName}")]
     Task<FileInfo> GetFileInfoAsync(string bucketName, string objectName, CancellationToken cancellationToken = default);

     [Headers("Accept: application/octet-stream")]
     [Get("/v1/File/DownloadFile/{bucketName}/{objectName}")]
     Task DownloadFileAsync(string bucketName, string objectName, [Query] string versionId, CancellationToken cancellationToken = default);

     [Multipart]
     [Headers("Accept: application/json")]
     [Post("/v1/File/UploadFile/{bucketName}/{directory}")]
     Task UploadFileAsync(string bucketName, string directory, StreamPart file, CancellationToken cancellationToken = default);

     [Headers("Accept: application/json")]
     [Post("/v1/File/CopyFile/{fromBucketName}/{filePath}/{toDirectory}")]
     Task CopyFileAsync(string fromBucketName, string filePath, string toDirectory, [Query] string toBucketName, CancellationToken cancellationToken = default);

     [Headers("Accept: application/json")]
     [Post("/v1/File/MoveFile/{fromBucketName}/{fromFilePath}/{toFilePath}")]
     Task MoveFileAsync(string fromBucketName, string fromFilePath, string toFilePath, [Query] string toBucketName, CancellationToken cancellationToken = default);

     [Delete("/v1/File/DeleteFile/{bucketName}/{filePath}")]
     Task DeleteFileAsync(string bucketName, string filePath, CancellationToken cancellationToken = default);
 }

The endpoint in question is missing the long[] OrganizationalUnitIds which is a single property of the [FromForm] UploadFileArgs uploadFileArgs argument in the receiving endpoint. In line 545 of openApi spec the request body is defined as follows:

"requestBody": {
                    "content": {
                        "multipart/form-data": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "OrganizationalUnitIds": {
                                        "type": "array",
                                        "items": {
                                            "type": "integer",
                                            "format": "int64"
                                        }
                                    },
                                    "file": {
                                        "type": "string",
                                        "format": "binary"
                                    }
                                }
                            },
                            "encoding": {
                                "OrganizationalUnitIds": {
                                    "style": "form"
                                },
                                "file": {
                                    "style": "form"
                                }
                            }
                        }
                    }
                }

Support Key: [h/tnek2]
The support key is a unique identifier generated on the machine which is used when logging telemetry data. This feature is introduced in v0.5.4 and is shown in the output when running Refitter.

openapispec.json

Remove unused schema definitions (e.g. `--remove-unreferenced-schema` )

To reduce the amount of generated Contracts, we could walk the paths and remove and non-referenced schema

As we already are able to filter paths, we can make use of that to clean up the spec and generate less code.

Example code for cleaning up the schema definitions

// Uses Microsoft.OpenApi.Readers & SwashBuckle.AspNetCore for Reading and Writing specs

void Main()
{
    var rdr = new Microsoft.OpenApi.Readers.OpenApiStreamReader();
    using var specFile = File.OpenRead(@"C:\spec.json");

    // doc is a "filtered" OpenApi-spec
    var doc = rdr.Read(specFile, out var diagnostic);

    var schemaCleaner = new SchemaCleaner();
    schemaCleaner.RemoveUnreferencedSchema(doc);

    using var tw = File.CreateText(@"C:\clean_spec.yaml");
    var writer = new Microsoft.OpenApi.Writers.OpenApiYamlWriter(tw);
    doc.SerializeAsV3(writer);
}


public class SchemaCleaner
{
    public void RemoveUnreferencedSchema(OpenApiDocument doc)
    {
        var usage = FindUsedSchema(doc);

        var unused = doc.Components.Schemas.Where(s => !usage.Contains(s.Key))
            .ToArray();
        foreach (var unusedSchema in unused)
        {
            doc.Components.Schemas.Remove(unusedSchema);
        }
    }

    HashSet<string> FindUsedSchema(OpenApiDocument doc)
    {
        var toProcess = new Stack<OpenApiSchema>();

        foreach (var (_, pathItem) in doc.Paths)
        {
            foreach (var p in pathItem.Parameters)
            {
                TryPush(p.Schema, toProcess);
            }

            foreach (var (_, op) in pathItem.Operations)
            {
                foreach (var (_, resp) in op.Responses)
                {
                    foreach (var (_, header) in resp.Headers)
                    {
                        TryPush(header.Schema, toProcess);
                    }
                    foreach (var (_, content) in resp.Content)
                    {
                        TryPush(content.Schema, toProcess);
                    }
                }
            }
        }

        var seen = new HashSet<string>();
        while (toProcess.TryPop(out var schema))
        {
            if (schema.Reference?.Id is { } refId)
            {
                if (!seen.Add(refId))
                {
                    // prevent recursion
                    continue;
                }
            }
            foreach (var subSchema in EnumerateSchema(schema))
            {
                TryPush(subSchema, toProcess);
            }
        }

        return seen;
    }

    private void TryPush(OpenApiSchema? schema, Stack<OpenApiSchema> stack)
    {
        if (schema == null)
        {
            return;
        }
        stack.Push(schema);
    }

    IEnumerable<OpenApiSchema> EnumerateSchema(OpenApiSchema? schema)
    {
        if (schema is null)
        {
            return Enumerable.Empty<OpenApiSchema>();
        }

        return EnumerateInternal(schema)
            .Where(x => x != null)
            .Select(x => x!);

        static IEnumerable<OpenApiSchema?> EnumerateInternal(OpenApiSchema schema)
        {
            yield return schema.AdditionalProperties;
            yield return schema.Items;
            yield return schema.Not;

            foreach (var subSchema in schema.AllOf)
            {
                yield return subSchema;
            }
            foreach (var subSchema in schema.AnyOf)
            {
                yield return subSchema;
            }
            foreach (var subSchema in schema.OneOf)
            {
                yield return subSchema;
            }
            foreach (var (_, subSchema) in schema.Properties)
            {
                yield return subSchema;
            }
        }

    }
}

Add SourceGenerator support to the standalone tool

Is your feature request related to a problem? Please describe.
To enable the generated client to have the serializers source generated, the tool could generate an <ApiTypeName>JsonSerializerContext class which inherits from JsonSerializerContext and add all the type names with JsonSerializable to the class, so the serializer source generator could kick in and do its job.

This feature can only be added to the standalone tool because source generators cannot interact with each other currently.

Describe the solution you'd like
An <ApiTypeName>JsonSerializerContext class would be generated as well.

Describe alternatives you've considered
Manually write this class by hand which is tedious.

Additional context
More info on it: https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-source-generator/

Parameters' casing in openAPI document are not honoured in Refit interface methods

When Refitter generates an operation, the parameter names in the methods are formatted in snakeCase.
Here is an example with Username and Password parameter:

OpenAPI Spec

{
  "/user/login": {
    "get": {
      "tags": [
        "user"
      ],
      "summary": "Logs user into the system",
      "description": "",
      "operationId": "loginUser",
      "parameters": [
        {
          "name": "Username",
          "in": "query",
          "description": "The user name for login",
          "required": false,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "Password",
          "in": "query",
          "description": "The password for login in clear text",
          "required": false,
          "schema": {
            "type": "string"
          }
        }
      ],
      ...
    }
  }
}

Generated Code

        [Get("/user/login")]
        Task<string> LoginUser([Query] string username, [Query] string password);

URL

http://swagger.io/user/login?username=XXXX&password=XXXX

This is causing issues in APIs that require the original casing for parameter names ( Username instead of username and Password instead of password)

Support for .net6.0 / Reasoning why .net7 is required

i don't quite get the reasoning why the LTS net6.0 is not supported.

i can't install this tool into our build, because we develop on "LTS"s only.
Current workaround is to install the tool globally if net7.0 is installed and use the global executable.

Add support for using .refitter file from CLI

Hi!

Is it possible for add a support for using file from CLI params?
Like this:

refitter ./openapi.json --settings-file cli.json

I saw you added .refitter file for setup source generation, but some of the parameters weren't supported in the CLI. For example "naming" param.

It would be soo cool to send just file to the command line instead of passing params.

Model definition with property named System results in class that does not compile

Describe the bug
When generating a client for an OpenApi (swagger version 2.0) with a model definition with a property named System, the generated code no longer compiles. Reason being that the same class also uses references to other classes from the System namespace.

Support Key: mmt1dt0

OpenAPI Specifications
(not the full specs, but this is the gist of it)

{
  "swagger": "2.0",
  "info": {
    "title": "XX",
    "version": "0.0.0"
  },
  "host": "x.io",
  "basePath": "/",
  "schemes": [
    "https"
  ],
  "definitions": {
    "Dummy": {
      "type": "object",
      "properties": {
        "system": {
          "type": "string",
          "description": "XX"
        }
      },
      "description": "XXX",
      "example": {
        "system": "Dummy"
      }
    }
  }
},

Additional context
May also be true for some reserved words. System is not really a reserved word, but just not very practical.

Base type not generated for types specified by oneOf in the schema

Describe the bug
I try to generate a client for the FusionAuth spec and it is using oneOf for IdentityProviderField and in the resulting source file no IdentityProvider base class is generated.

Support Key: [my-support-key]
N/A (did not find it in output)

OpenAPI Specifications
https://raw.githubusercontent.com/FusionAuth/fusionauth-openapi/main/openapi.yaml

Additional context
My quick fix were these:

  1. Create an IdentityProvider class:
public class IdentityProvider
{
}
  1. Change the method response to have a generic parameter:
    public partial class IdentityProviderResponse<T> where T: IdentityProvider
    {

        [JsonPropertyName("identityProvider")]

        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]   
        public T IdentityProvider { get; set; }

        [JsonPropertyName("identityProviders")]

        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]   
        public ICollection<T> IdentityProviders { get; set; }

        private IDictionary<string, object> _additionalProperties;

        [JsonExtensionData]
        public IDictionary<string, object> AdditionalProperties
        {
            get { return _additionalProperties ?? (_additionalProperties = new Dictionary<string, object>()); }
            set { _additionalProperties = value; }
        }
    }
  1. Change the method signature:
Task<IdentityProviderResponse<T>> RetrieveIdentityProviderWithIdAsync<T>(string identityProviderId, CancellationToken cancellationToken = default) where T : IdentityProvider;

Member with the same signature is already declared

Describe the bug
The following command:

refitter https://api.hubspot.com/api-catalog-public/v1/apis/webhooks/v3 --namespace HubSpot --output ./GeneratedCode.cs

Generates the following:

    public interface IWebhooksAPI
    {

        [Get("/webhooks/v3/{appId}/settings")]
        Task<SettingsResponse> GetAll(int appId);

        [Put("/webhooks/v3/{appId}/settings")]
        Task<SettingsResponse> Configure(int appId, [Body] SettingsChangeRequest body);

        [Delete("/webhooks/v3/{appId}/settings")]
        Task Clear(int appId);

        [Get("/webhooks/v3/{appId}/subscriptions")]
        Task<SubscriptionListResponse> GetAll(int appId);

        [Post("/webhooks/v3/{appId}/subscriptions")]
        Task Create(int appId, [Body] SubscriptionCreateRequest body);

        [Post("/webhooks/v3/{appId}/subscriptions/batch/update")]
        Task<BatchResponseSubscriptionResponse> UpdateBatch(int appId, [Body] BatchInputSubscriptionBatchUpdateRequest body);

        [Get("/webhooks/v3/{appId}/subscriptions/{subscriptionId}")]
        Task<SubscriptionResponse> GetById(int subscriptionId, int appId);

        [Delete("/webhooks/v3/{appId}/subscriptions/{subscriptionId}")]
        Task Archive(int subscriptionId, int appId);

        [Patch("/webhooks/v3/{appId}/subscriptions/{subscriptionId}")]
        Task<SubscriptionResponse> Update(int subscriptionId, int appId, [Body] SubscriptionPatchRequest body);

    }

Here there are two GetAll methods defined that are clashing.

Support Key: kedfgny

OpenAPI Specifications
https://api.hubspot.com/api-catalog-public/v1/apis/webhooks/v3

Also happens with https://api.hubspot.com/api-catalog-public/v1/apis/crm/v3/properties

Improving documentation for --settings-file cli tool parameter

Per documentation file for --settings-file: .. Specifying this option will ignore all other settings.

This does not seem to be precisely correct as --output parameter is still taken into account and allows to specify a name for a generated output file. This is a welcome feature as output file name doesn't seem to be configurable via a `.refitter' file.

Maybe it would help to reflect the option in the documentation.

OperationHeaders generation problem with headers containing a -

When generating Refit Interfaces for operations with header parameters, if the header key contains a - , the resulting code contains an AliasAs, which is ignored by Refit when serializing the header parameters.

Example

parameters:
  - name: api-key
    in: header
    description: ''
    required: false
    schema:
      type: string
   [Header("api_key"), AliasAs("api-key")] string api_key

DirectoryNotFoundException

Describe the bug
Code generation fails if the output folder doesn't exist

Support Key: ptbddgt

OpenAPI Specifications
https://petstore3.swagger.io/api/v3/openapi.json

Additional context:

$ refitter https://petstore3.swagger.io/api/v3/openapi.json --output ../Boom/Kaboom/GeneratedCode.cs

Exception message
Could not find a part of the path 'C:\Boom\Kaboom\GeneratedCode.cs'.

Stack trace

System.IO.DirectoryNotFoundException: Could not find a part of the path 'C:\Boom\Kaboom\GeneratedCode.cs'.
at Microsoft.Win32.SafeHandles.SafeFileHandle.CreateFile(System.String fullPath, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, System.IO.FileOptions options) at offset 161
at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(System.String fullPath, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, System.IO.FileOptions options, System.Int64 preallocationSize) at offset 6
at System.IO.Strategies.OSFileStreamStrategy..ctor(System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, System.IO.FileOptions options, System.Int64 preallocationSize) at offset 49
at System.IO.Strategies.FileStreamHelpers.ChooseStrategyCore(System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, System.IO.FileOptions options, System.Int64 preallocationSize) at offset 24
at System.IO.Strategies.FileStreamHelpers.ChooseStrategy(System.IO.FileStream fileStream, System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, System.Int32 bufferSize, System.IO.FileOptions options, System.Int64 preallocationSize)
at System.IO.File.AsyncStreamWriter(System.String path, System.Text.Encoding encoding, System.Boolean append)
at System.IO.File.WriteAllTextAsync(System.String path, System.String contents, System.Text.Encoding encoding, System.Threading.CancellationToken cancellationToken) at offset 85
at Refitter.GenerateCommand.<ExecuteAsync>d__1.MoveNext() at offset 397 in /home/runner/work/refitter/refitter/src/Refitter/GenerateCommand.cs:line 45:col 13

The client parameter type's names occur wrong when multipart is include.

image

sample openapi:
animal.json

sample refitter config file:
Animal.refitter
{
"openApiPath": "./animal.json",
"namespace": "Animal",
"naming": {
"useOpenApiTitle": false
},
"multipleInterfaces": "ByTag",
"operationNameTemplate": "{operationName}Async",
"outputFolder": "../Clients",
"outputFilename": "Animal.cs",
"codeGeneratorSettings": {
"requiredPropertiesMustBeDefined": true,
"generateDataAnnotations": true,
"anyType": "object",
"dateType": "System.DateTimeOffset",
"dateTimeType": "System.DateTimeOffset",
"timeType": "System.TimeSpan",
"timeSpanType": "System.TimeSpan",
"arrayType": "System.Collections.Generic.ICollection",
"dictionaryType": "System.Collections.Generic.IDictionary",
"arrayInstanceType": "System.Collections.ObjectModel.Collection",
"dictionaryInstanceType": "System.Collections.Generic.Dictionary",
"arrayBaseType": "System.Collections.ObjectModel.Collection",
"dictionaryBaseType": "System.Collections.Generic.Dictionary",
"propertySetterAccessModifier": "",
"generateImmutableArrayProperties": false,
"generateImmutableDictionaryProperties": false,
"handleReferences": false,
"jsonSerializerSettingsTransformationMethod": null,
"generateJsonMethods": false,
"enforceFlagEnums": false,
"inlineNamedDictionaries": false,
"inlineNamedTuples": true,
"inlineNamedArrays": false,
"generateOptionalPropertiesAsNullable": false,
"generateNullableReferenceTypes": false,
"generateNativeRecords": false,
"generateDefaultValues": true,
"inlineNamedAny": false,
"excludedTypeNames": []
}
}

Unexpected initial token 'Boolean' when populating object

Describe the bug
refitter https://api.getport.io/json -o IPortApi.cs
Refitter v0.7.3.37
Support key: 6mxy0om

Error:
Unexpected initial token 'Boolean' when populating object. Expected JSON object or array. Path
'paths./v1/blueprints/{blueprint_identifier}/permissions.patch.requestBody.content.application/json.schema.properties.ad
ditionalProperties', line 1, position 3314.
Error: Could not find color or style 'T'.

Support Key: 6mxy0om

OpenAPI Specifications
https://api.getport.io/json

Add `CancellationToken cancellationToken = default` to generated Methods

Currently the generated code looks like this

[Post("/v1/Endpoint")]
Task EndpointPOST([Body] EndpointV1 body);

To support timeouts etc. it would be nice to get refitter to add the CancellationToken-parameter like this

[Post("/v1/Endpoint")]
Task EndpointPOST([Body] EndpointV1 body, CancellationToken cancellationToken = default);

Adding security schemes to the api interface generator

I'm currently looking at adding security schemes to the Api generator, but am unsure how to handle it. I suspect that it have to be separate functions, or maybe seperate interfaces, for each securityscheme defined one? But I'm unsure what would be the better way forward and wanted to discuss it.

Config parameter to add suffix to contract types

Is your feature request related to a problem? Please describe.
To match our existing coding conventions and to increase code readability, it would be great to have a config parameter that allows to set a suffix to the generated contract types (like Dto --> PetDto).

Additional context
Thank you for your help.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.