gnostic icon indicating copy to clipboard operation
gnostic copied to clipboard

openapi v3 petstore Pets not an array.

Open bhenderson opened this issue 6 years ago • 1 comments

I tried the petstore example. Pets which is supposed to be an array of Pet, is being generated as an empty struct. Here is a dump of the Type

   (*surface_v1.Type)(0xc00006e960)({
    Name: (string) (len=4) "Pets",
    Kind: (surface_v1.TypeKind) 0,
    Description: (string) (len=41) "implements the service definition of Pets",
    ContentType: (string) "",
    Fields: ([]*surface_v1.Field) <nil>,
    TypeName: (string) (len=4) "Pets"
   }),

bhenderson avatar Feb 06 '19 21:02 bhenderson

Digging into it, this is what I've come up with. I wanted to see if I was on the right track before submitting a PR.

diff --git a/plugins/gnostic-go-generator/render_types.go b/plugins/gnostic-go-generator/render_types.go
index e77707e..aacf56f 100644
--- a/plugins/gnostic-go-generator/render_types.go
+++ b/plugins/gnostic-go-generator/render_types.go
@@ -42,6 +42,8 @@ func (renderer *Renderer) RenderTypes() ([]byte, error) {
                        f.WriteLine(`}`)
                } else if modelType.Kind == surface.TypeKind_OBJECT {
                        f.WriteLine(`type ` + modelType.TypeName + ` map[string]` + modelType.ContentType)
+               } else if modelType.Kind == 2 {
+                       f.WriteLine(`type ` + modelType.TypeName + ` []` + modelType.ContentType)
                } else {
                        f.WriteLine(`type ` + modelType.TypeName + ` interface {}`)
                }
diff --git a/surface/model_openapiv3.go b/surface/model_openapiv3.go
index 70e0393..74146ea 100644
--- a/surface/model_openapiv3.go
+++ b/surface/model_openapiv3.go
@@ -104,6 +104,10 @@ func (b *OpenAPI3Builder) buildTypeFromSchemaOrReference(
                                t.ContentType = typeForRef(schema.AdditionalProperties.GetSchemaOrReference().GetReference().GetXRef())
                        }
                }
+               if schema.Type == "array" && schema.Items != nil {
+                       t.Kind = 2
+                       _, t.ContentType, _ = b.typeForSchema(schema)
+               }
                return t, err
        } else {
                return nil, errors.New("unable to determine service type for referenced schema " + name)

The only thing I could think of was creating a new TypeKind. I've tried adding it to the surface.proto but when I run protoc I get lots of changes besides just the new TypeKind.

bhenderson avatar Feb 09 '19 06:02 bhenderson

This looks like a bug in encoding content/type. For example if we look at findPetsByStatus list response:

  • v3: https://petstore3.swagger.io/#/pet/findPetsByStatus
    "/pet/findByStatus": {
      "get": {
        "summary": "Finds Pets by status",
        "description": "Multiple status values can be provided with comma separated strings",
        "operationId": "findPetsByStatus",
        "parameters": [],
        "responses": {
          "200": {
            "description": "successful operation",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Pet"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Pet"
                  }
                }
              }
            }
          }
        }
      }
    }
  • v2: https://petstore.swagger.io/#/pet/findPetsByStatus
    "/pet/findByStatus": {
      "get": {
        "summary": "Finds Pets by status",
        "description": "Multiple status values can be provided with comma separated strings",
        "operationId": "findPetsByStatus",
        "produces": [
          "application/json",
          "application/xml"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "successful operation",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/definitions/Pet"
              }
            }
          }
        }
      }
    }

I think these two methods should produce the same surface model. Current behaviour (encoding surface.Model as json) shows v2 ignores the produces field:

    {
      "name": "ListPetsResponses",
      "description": "ListPetsResponses holds responses of ListPets",
      "fields": [
        {
          "name": "200",
          "type": "Pet",
          "kind": "ARRAY"
        }
      ]
    }

whilst v3 tries to encode the application/json creating a layer of indirection:

    {
      "name": "listPetsOK",
      "fields": [
        {
          "name": "application/json",
          "type": "Pet",
          "kind": "ARRAY"
        }
      ]
    },
    {
      "name": "ListPetsResponses",
      "description": "ListPetsResponses holds responses of ListPets",
      "fields": [
        {
          "name": "200",
          "type": "listPetsOK",
          "kind": "REFERENCE"
        }
      ]
    }

I think the fields need to be keyed on HTTP status and content/type. Would 200:application/json be okay?

emcfarlane avatar Apr 10 '23 22:04 emcfarlane

Similar for parameters too, would be a nice to combine them to make the surface model generate the same code for both V2 and V3. v3:

types: {
  name: "updatePetRequestBody"
  fields: {
    name: "application/json"
    type: "Pet"
    kind: REFERENCE
  }
  fields: {
    name: "application/xml"
    type: "Pet"
    kind: REFERENCE
  }
  fields: {
    name: "application/x-www-form-urlencoded"
    type: "Pet"
    kind: REFERENCE
  }
}
types: {
  name: "UpdatePetParameters"
  description: "UpdatePetParameters holds parameters to UpdatePet"
  fields: {
    name: "request_body"
    type: "updatePetRequestBody"
    kind: REFERENCE
  }
}

v2:

types: {
  name: "UpdatePetParameters"
  description: "UpdatePetParameters holds parameters to UpdatePet"
  fields: {
    name: "body"
    type: "Pet"
    kind: REFERENCE
  }
}

emcfarlane avatar Apr 12 '23 08:04 emcfarlane