Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not working well with OpenAPI Generator #10356

Open
stevefan1999-personal opened this issue Jul 8, 2023 · 1 comment
Open

Not working well with OpenAPI Generator #10356

stevefan1999-personal opened this issue Jul 8, 2023 · 1 comment
Labels

Comments

@stevefan1999-personal
Copy link

stevefan1999-personal commented Jul 8, 2023

I really want to make a discussion thread instead of putting this as an issue but whatever for now.

I want to generate a C# OpenAPI client through the use of OpenAPI and the OpenAPI Generator, here's the command I used:

openapi-generator-cli generate `
    -g csharp `
    -i https://osu.ppy.sh/docs/openapi.yaml `
    -c config.yaml

config.yaml:

packageName: OsuApi.V2.OpenAPI.Generated
returnICollection: true
targetFramework: netstandard2.1
useCollection: true
nullableReferenceTypes: true
netCoreProjectFile: true
packageAuthors: stevefan1999
packageVersion: 0.0.1
sourceFolder: .
useDateTimeOffset: true
validatable: false
optionalEmitDefaultValues: true
library: httpclient

I did generate a pretty nice-looking C# OpenAPI library (maybe make it officially generated by peppy someday), but there is a couple of problems along the journey.

First it seems like HTML inside an example is not parsed correctly:

"message_html": "<div class='changelog-md'><p class=\"changelog-md__paragraph\">New seasonal backgrounds ahoy! Amazing work by the artists.</p>\n</div>",

Results in this:

        /// <summary>
        /// Gets or Sets MessageHtml
        /// </summary>
        /// <example>&lt;div class&#x3D;&#39;changelog-md&#39;&gt;&lt;p class&#x3D;&quot;changelog-md__paragraph&quot;&gt;New seasonal backgrounds ahoy! Amazing work by the artists.&lt;/p&gt;
&lt;/div&gt;</example>
        [DataMember(Name = "message_html", EmitDefaultValue = true)]
        public string MessageHtml { get; set; }

Another problem is that the response format for TopicsController\show is not well formed:

* @response {
* "topic": { "id": 1, "...": "..." },
* "posts": [
* { "id": 1, "...": "..." },
* { "id": 2, "...": "..." }
* ],
* "cursor_string": "eyJoZWxsbyI6IndvcmxkIn0",
* "sort": "id_asc"
* }
*/

Which results in this:

        /// <summary>
        /// Initializes a new instance of the <see cref="GetTopicAndPosts200ResponseTopic" /> class.
        /// </summary>
        /// <param name="id">id.</param>
        /// <param name="">.</param>
        public GetTopicAndPosts200ResponseTopic(int id = default(int), string  = default(string))
        {
            this.Id = id;
            this.___ = ;
        }

After fixing both problem we see another problem with body parameter not well-parsable:

    /// <summary>
        /// Initializes a new instance of the <see cref="GetBeatmapAttributesRequest" /> class.
        /// </summary>
        /// <param name="mods">Mod combination. Can be either a bitset of mods, array of mod acronyms, or array of mods. Defaults to no mods..</param>
        /// <param name="ruleset">Ruleset of the difficulty attributes. Only valid if it&#39;s the beatmap ruleset or the beatmap can be converted to the specified ruleset. Defaults to ruleset of the specified beatmap..</param>
        /// <param name="rulesetId">The same as &#x60;ruleset&#x60; but in integer form..</param>
        public GetBeatmapAttributesRequest(Collection<NumberStringMod> mods = default(Collection<NumberStringMod>), GameMode ruleset = default(GameMode), int rulesetId = default(int))
        {
            this.Mods = mods;
            this.Ruleset = ruleset;
            this.RulesetId = rulesetId;
        }

        /// <summary>
        /// Mod combination. Can be either a bitset of mods, array of mod acronyms, or array of mods. Defaults to no mods.
        /// </summary>
        /// <value>Mod combination. Can be either a bitset of mods, array of mod acronyms, or array of mods. Defaults to no mods.</value>
        /// <example>1</example>
        [DataMember(Name = "mods", EmitDefaultValue = true)]
        public Collection<NumberStringMod> Mods { get; set; }

        /// <summary>
        /// Ruleset of the difficulty attributes. Only valid if it&#39;s the beatmap ruleset or the beatmap can be converted to the specified ruleset. Defaults to ruleset of the specified beatmap.
        /// </summary>
        /// <value>Ruleset of the difficulty attributes. Only valid if it&#39;s the beatmap ruleset or the beatmap can be converted to the specified ruleset. Defaults to ruleset of the specified beatmap.</value>
        /// <example>osu</example>
        [DataMember(Name = "ruleset", EmitDefaultValue = true)]
        public GameMode Ruleset { get; set; }

/**
* Get Beatmap Attributes
*
* Returns difficulty attributes of beatmap with specific mode and mods combination.
*
* ---
*
* ### Response format
*
* Field | Type
* ---------- | ----
* Attributes | [DifficultyAttributes](#beatmapdifficultyattributes)
*
* @urlParam beatmap integer required Beatmap id. Example: 2
* @bodyParam mods number|string[]|Mod[] Mod combination. Can be either a bitset of mods, array of mod acronyms, or array of mods. Defaults to no mods. Example: 1
* @bodyParam ruleset GameMode Ruleset of the difficulty attributes. Only valid if it's the beatmap ruleset or the beatmap can be converted to the specified ruleset. Defaults to ruleset of the specified beatmap. Example: osu
* @bodyParam ruleset_id integer The same as `ruleset` but in integer form. No-example
*
* @response {
* "attributes": {
* "max_combo": 100,
* ...
* }
* }
*/
public function attributes($id)

This is however not a solvable issue because C# did not support discriminated union which is proposed (but it exists in F#):
https://github.com/dotnet/csharplang/blob/main/proposals/discriminated-unions.md

So this is not an easy problem to solve for now. I simply just yeeted all NumberStringMod into string and hope it will work. After that all the source code can be compiled and I got a semi-working osu API v2 client in C#!

@jimschubert Should I cross reference this to https://github.com/OpenAPITools/openapi-generator team?

Another small issue is that I did not see the GameMode definition in the OpenAPI schema either so the class GameMode for the Ruleset doesn't really exists for some reason, but it is here as an enum: https://github.com/ppy/osu-web/blob/HEAD/resources/js/interfaces/game-mode.ts

@nanaya
Copy link
Collaborator

nanaya commented Jul 10, 2023

I don't think anyone ever looked into it before 👀 it's just part of the document generator

@peppy peppy added the type:api label Aug 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants