snipe / snipe-it

A free open source IT asset/license management system
https://snipeitapp.com
GNU Affero General Public License v3.0
10.92k stars 3.16k forks source link

GoLang OpenAPI has no types #12520

Open Smithx10 opened 1 year ago

Smithx10 commented 1 year ago

Debug mode

Describe the bug

Running

java -jar ./openapi-generator-cli-6.3.0.jar generate -i ./snipe-it/snipe-it-v6.0.14.json -g go -o ./test

Results in a Go Library that has no types only interface{}.

for example:

resp, r, err := apiClient.DefaultApi.Fields1(auth).Execute()
if err != nil {
    fmt.Fprintf(os.Stderr, "Error when calling `DefaultApi.Fields1``: %v\n", err)
    fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r)
}

Only returns:

type Fields1200Response struct {
    Total interface{} `json:"total,omitempty"`
    Rows  interface{} `json:"rows,omitempty"`
}

I'd imagine we should be able to do better than this... Since a field is known.

    {
      "id": 67,
      "name": "swp32",
      "db_column_name": "_snipeit_swp32_67",
      "format": "ANY",
      "field_values": null,
      "field_values_array": null,
      "type": "text",
      "required": false,
      "display_in_user_view": false,
      "created_at": {
        "datetime": "2023-02-15 12:35:26",
        "formatted": "2023-02-15 12:35 PM"
      },
      "updated_at": {
        "datetime": "2023-02-15 12:35:26",
        "formatted": "2023-02-15 12:35 PM"
      }


### Reproduction steps

1.  wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/6.3.0/openapi-generator-cli-6.3.0.jar
2.  mkdir ./test && java -jar ./openapi-generator-cli-6.3.0.jar generate -i https://snipe-it.readme.io/openapi/6393d2bdcb07bf002a8d3e1e -g go -o ./test

3. See no types
...

### Expected behavior

There should be types

### Screenshots

_No response_

### Snipe-IT Version

6.0.14

### Operating System

Centos-9-stream

### Web Server

Whatever is in the Docker

### PHP Version

Whatever is in the Docker

### Operating System

_No response_

### Browser

_No response_

### Version

_No response_

### Device

_No response_

### Operating System

_No response_

### Browser

_No response_

### Version

_No response_

### Error messages

_No response_

### Additional context

_No response_
welcome[bot] commented 1 year ago

👋 Thanks for opening your first issue here! If you're reporting a 🐞 bug, please make sure you include steps to reproduce it. We get a lot of issues on this repo, so please be patient and we will get back to you as soon as we can.

Smithx10 commented 1 year ago

Following up with some information:

I made a subset of the spec and made small adjustments to see what behavior changed. Here are my results:

Each Test was generated with:

java -jar ../..//openapi-generator-cli-6.3.0.jar generate -i ./fields.json -g go -o ./output

OpenAPI 3.1.0:

{
  "openapi": "3.1.0",
  "info": {
    "title": "Snipe-IT v6 API",
    "version": "6.0.14"
  },
  "servers": [
    {
      "url": "https://develop.snipeitapp.com/api/v1"
    }
  ],
  "paths": {
    "/foo": {
      "get": {
        "summary": "/foo",
        "description": "Returns a listing of available custom foo",
        "operationId": "foo-1",
        "responses": {
          "200": {
            "description": "200",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/field"
                }
              }
            }
          }
        },
        "deprecated": false
      }
    }
  },
  "components": {
    "securitySchemes": {},
    "schemas": {
      "field": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer",
            "example": 6,
            "default": 0
          },
          "rows": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "integer",
                  "example": 1,
                  "default": 0
                },
                "name": {
                  "type": "string",
                  "example": "IMEI"
                }
              }
            }
          }
        }
      }
    }
  }
}

Resulted in: cat output/model_field.go | head -n 25


/*
Snipe-IT v6 API

No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)

API version: 6.0.14
*/

// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.

package openapi

import (
        "encoding/json"
)

// checks if the Field type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &Field{}

// Field struct for Field
type Field struct {
        Total interface{} `json:"total,omitempty"`
        Rows interface{} `json:"rows,omitempty"`
}

Changing just .openapi to 3.0.0 resulted in types....

{
  "openapi": "3.0.0",
  "info": {
    "title": "Snipe-IT v6 API",
    "version": "6.0.14"
  },
  "servers": [
    {
      "url": "https://develop.snipeitapp.com/api/v1"
    }
  ],
  "paths": {
    "/foo": {
      "get": {
        "summary": "/foo",
        "description": "Returns a listing of available custom foo",
        "operationId": "foo-1",
        "responses": {
          "200": {
            "description": "200",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/field"
                }
              }
            }
          }
        },
        "deprecated": false
      }
    }
  },
  "components": {
    "securitySchemes": {},
    "schemas": {
      "field": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer",
            "example": 6,
            "default": 0
          },
          "rows": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "integer",
                  "example": 1,
                  "default": 0
                },
                "name": {
                  "type": "string",
                  "example": "IMEI"
                }
              }
            }
          }
        }
      }
    }
  }
}

cat output/model_field.go | head -n 25

/*
Snipe-IT v6 API

No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)

API version: 6.0.14
*/

// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.

package openapi

import (
        "encoding/json"
)

// checks if the Field type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &Field{}

// Field struct for Field
type Field struct {
        Total *int32 `json:"total,omitempty"`
        Rows []FieldRowsInner `json:"rows,omitempty"`
}

cat output/model_field_rows_inner.go | head -26

/*
Snipe-IT v6 API

No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)

API version: 6.0.14
*/

// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.

package openapi

import (
        "encoding/json"
)

// checks if the FieldRowsInner type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &FieldRowsInner{}

// FieldRowsInner struct for FieldRowsInner
type FieldRowsInner struct {
        Id *int32 `json:"id,omitempty"`
        Name *string `json:"name,omitempty"`
}
Smithx10 commented 1 year ago

By changing the "openapi" version from 3.1.0 -> 3.0.0 in the openapi json located here https://snipe-it.readme.io/openapi/6393d2bdcb07bf002a8d3e1e, I was able to get types but had to append "--skip-validate-spec"

java -jar ..//openapi-generator-cli-6.3.0.jar generate -i ./snipe-it-v6.0.14.json -g go -o ./output --skip-validate-spec

edit with --skip-validate-spec, you end up with something that isn't go / complete.

./model_fieldsetsid_200_response_one_of_models.go:20:9: undefined: Array ./model_fieldsetsid_200_response_one_of_models.go:77:55: undefined: Array ./model_fieldsetsid_200_response_one_of_models.go:79:11: undefined: Array ./model_fieldsetsid_200_response_one_of_models.go:87:59: undefined: Array ./model_fieldsetsid_200_response_one_of_models.go:104:55: undefined: Array ./model_hardware_by_asset_tag_200_response_one_of.go:45:20: undefined: Array ./model_hardware_by_asset_tag_200_response_one_of.go:911:64: undefined: Array ./model_hardware_by_asset_tag_200_response_one_of.go:913:11: undefined: Array ./model_hardware_by_asset_tag_200_response_one_of.go:921:68: undefined: Array ./model_hardware_by_asset_tag_200_response_one_of.go:938:64: undefined: Array ./model_hardware_by_asset_tag_200_response_one_of.go:913:11: too many errors

Either way, I believe we are probably running into some difference in 3.0.0 -> 3.1.0 in regards to schema and golang types.

snipe commented 1 year ago

@uberbrady this one's all you, bud ;)

Smithx10 commented 1 year ago

@snipe,

It may be as simple as openapi-generator-cli-6.3.0.jar doesn't follow the spec.

Using https://github.com/deepmap/oapi-codegen seems to result in what I would expect.

 cat snipe-it-v6.0.14.json | jq .openapi
"3.1.0"
rm -rf foo.gen.go
oapi-codegen --package=main snipe-it-v6.0.14.json > foo.gen.go
type Fields1Response struct {
    Body         []byte
    HTTPResponse *http.Response
    JSON200      *struct {
        Rows *[]struct {
            CreatedAt *struct {
                Datetime  *string `json:"datetime,omitempty"`
                Formatted *string `json:"formatted,omitempty"`
            } `json:"created_at,omitempty"`
            DbColumnName     *string      `json:"db_column_name,omitempty"`
            FieldValues      *interface{} `json:"field_values,omitempty"`
            FieldValuesArray *interface{} `json:"field_values_array,omitempty"`
            Format           *string      `json:"format,omitempty"`
            Id               *int         `json:"id,omitempty"`
            Name             *string      `json:"name,omitempty"`
            Required         *bool        `json:"required,omitempty"`
            Type             *string      `json:"type,omitempty"`
            UpdatedAt        *struct {
                Datetime  *string `json:"datetime,omitempty"`
                Formatted *string `json:"formatted,omitempty"`
            } `json:"updated_at,omitempty"`
        } `json:"rows,omitempty"`
        Total *int `json:"total,omitempty"`
    }
    JSON400 *map[string]interface{}
}
GitHub
GitHub - deepmap/oapi-codegen: Generate Go client and server boilerplate from OpenAPI 3 specifications
Generate Go client and server boilerplate from OpenAPI 3 specifications - GitHub - deepmap/oapi-codegen: Generate Go client and server boilerplate from OpenAPI 3 specifications
uberbrady commented 1 year ago

So is the only change that you had to make was changing the version from 3.0.0 to 3.1.0 in our file? What happens if you back that out and then run it through the same tool? If so - then we don't have to do anything - yay! :)

Smithx10 commented 1 year ago

@uberbrady,

At the moment, I'm just trying to find a Generator that can generator the Go Client Library that has types. This is proving more difficult than I suspected it would be. "_"... Guess this is what happens when the folks that make the spec... don't make the tooling. (No clue.... I don't use swagger / openapi often) . :( :(

Looks like openapi-generator doesn't support 3.1.0. https://github.com/OpenAPITools/openapi-generator/issues/9083

So.... I think we probably want to have some documentation to note a "happy path" on how to gen some of these for users and if users go away from the "happy path" they are on their own.

I was able to generate a "a open api client library with types (Not sure if its a good one, but so far it seems reasonable...)" by running the following:

oapi-codegen --package=openapi snipe-it-v6.0.14.json > snipe-it.gen.go

the following code has no error handling, don't do this

package main

import (
    "context"
    "fmt"
    "log"

    oapi "git.my-company.com/my-org/go-snipe-it"
    "github.com/algorand/oapi-codegen/pkg/securityprovider"
)

func main() {
    bearerTokenProvider, bearerTokenProviderErr := securityprovider.NewSecurityProviderBearerToken("my-token")
    if bearerTokenProviderErr != nil {
        panic(bearerTokenProviderErr)
    }

    client, err := oapi.NewClientWithResponses("http://assets.my-org.my-company.net/api/v1", oapi.WithRequestEditorFn(bearerTokenProvider.Intercept))
    if err != nil {
        log.Fatal(err)
    }
    ctx := context.Background()

    resp, err := client.Fields1WithResponse(ctx)
    if err != nil {
        log.Fatal(err)
    }

    for _, field := range *resp.JSON200.Rows {
        fmt.Println("Name: ", *field.Name)
    }
}

This resulted in the following:

arch@9d46dd2d-1178-ce83-cbc4-d396e4a24060 ~/g/snipe-it-test ❯❯❯ go run main.go
Name:  MAC Address
Name:  swp1
Name:  swp2
Name:  swp3
Name:  swp4
Name:  swp5
Name:  swp6
Name:  swp7
Name:  swp8
Name:  swp9
Name:  swp10
Name:  swp11
Name:  swp12
Name:  swp13
Name:  swp14
Name:  swp15
Name:  swp16
Name:  swp17
Name:  swp18
Name:  swp19
Name:  swp20
Name:  swp21
Name:  swp22
Name:  swp23
Name:  swp24
Name:  swp25
Name:  swp26
Name:  swp27
Name:  swp28
Name:  swp29
Name:  swp30
Name:  swp31
Name:  swp32
StarlessNights commented 1 month ago

Snipe-IT's openapi documents are very inaccurate. IMO this is in large part due to the openapi spec not being a part of this repository.

I gave up with generators and just resorted to reverse engineering a rust library.