getkin / kin-openapi

OpenAPI 3.0 (and Swagger v2) implementation for Go (parsing, converting, validation, and more)
MIT License
2.56k stars 426 forks source link

LoadFromFile() doesn't seem to resolve in-file references #560

Open will-banked opened 2 years ago

will-banked commented 2 years ago

I've got an OpenAPI Spec file that looks like this

{
    "openapi": "3.0.0",
    "info": {
        "title": "API Spec",
        "version": "1.0.0"
    },
    "paths": {
        "/": {
            "post": {
                "summary": "Submit Domestic Payment Consent",
                "description": "",
                "operationId": "",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/OBWriteDomesticConsent4"
                            }
                        }
                    }
                },
                "responses": {
                    "201": {}
                }
            }
        }
    },
    "components": {
        "OBWriteDomesticConsent4": {
            "type": "object",
            "additionalProperties": false,
            "required": [
                "Data"
            ],
            "properties": {
                "Data": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                        "Initiation"
                    ],
                    "properties": {
                        "ReadRefundAccount": {
                            "description": "Specifies to share the refund account details with PISP",
                            "type": "string",
                            "enum": [
                                "No",
                                "Yes"
                            ]
                        },
                        "Initiation": {
                            "type": "object",
                            "additionalProperties": false,
                            "required": [
                                "InstructionIdentification",
                                "EndToEndIdentification",
                                "InstructedAmount",
                                "CreditorAccount"
                            ],
                            "properties": {
                                "InstructionIdentification": {
                                    "type": "string",
                                    "minLength": 1,
                                    "maxLength": 35
                                },
                                "EndToEndIdentification": {
                                    "type": "string",
                                    "minLength": 1,
                                    "maxLength": 35
                                },
                                "InstructedAmount": {
                                    "type": "object",
                                    "additionalProperties": false,
                                    "required": [
                                        "Amount",
                                        "Currency"
                                    ]
                                },
                                "DebtorAccount": {
                                    "type": "object",
                                    "additionalProperties": false,
                                    "required": [
                                        "SchemeName",
                                        "Identification"
                                    ],
                                    "properties": {
                                        "Name": {
                                            "type": "string",
                                            "minLength": 1,
                                            "maxLength": 350
                                        }
                                    }
                                },
                                "CreditorAccount": {
                                    "type": "object",
                                    "additionalProperties": false,
                                    "required": [
                                        "SchemeName",
                                        "Identification",
                                        "Name"
                                    ],
                                    "properties": {
                                        "Name": {
                                            "type": "string",
                                            "minLength": 1,
                                            "maxLength": 350
                                        }
                                    }
                                },
                                "RemittanceInformation": {
                                    "type": "object",
                                    "additionalProperties": false,
                                    "properties": {
                                        "Unstructured": {
                                            "type": "string",
                                            "minLength": 1,
                                            "maxLength": 140
                                        },
                                        "Reference": {
                                            "type": "string",
                                            "minLength": 1,
                                            "maxLength": 35
                                        }
                                    }
                                }
                            }
                        },
                        "Authorisation": {
                            "type": "object",
                            "additionalProperties": false,
                            "required": [
                                "AuthorisationType"
                            ],
                            "description": "The authorisation type request from the TPP.",
                            "properties": {
                                "AuthorisationType": {
                                    "description": "Type of authorisation flow requested.",
                                    "type": "string",
                                    "enum": [
                                        "Any",
                                        "Single"
                                    ]
                                },
                                "CompletionDateTime": {
                                    "type": "string",
                                    "format": "date-time"
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

The actual real spec is a lot longer with a lot more in-file references, but hopefully this is an example enough for now. In code I'm doing something like:

        doc, err := openapi3.NewLoader().LoadFromFile("./openapi/openapi.json")
    if err != nil {
        fmt.Println(err)
    }
    j, _ := doc.MarshalJSON()
    fmt.Println(string(j))

However, when Marshalling to JSON and printing it, all the refs are in output. I was under the impression that when loading in a file, references would be resolved. My assumption was that, at the very least, in-file refs would resolve, and any external files might then need a bit of additional work to sort out. But yeah, definitely expecting in-file ones to resolve, and not sure if they're not or if I'm doing something wrong.

I obviously have spotted the resolveRefsIn() function, but it seems like it takes a URL to what I assume is a location of external refs, which I'm not using.

Am I misunderstanding what Loader should resolve?

I've got a Go Playground there which uses a string instead of a .json file, and that actually does return an error in the console saying it can't resolve the ref, but I'm not getting any such errors when loading in the file itself.

Go playground: https://go.dev/play/p/Nds_KeSWGwd

xrstf commented 11 months ago

I ran across the same issue. It seems to me that kin-openapi prefers the Ref when marshalling the T back into JSON/YAML. The loader does resolve internal references by default (it calls .ResolveRefsIn(doc, nil)), but leaves the .Ref fields intact.

I was able to work around this like so (in a super dirty way that will blow up if your spec has circular references) https://gist.github.com/xrstf/cdfb3bfae8cc40aae32ea6b1b3270225