How to refer to 'subdocument' in when doing transformations with jq

Viewed 115

I'm trying to do a transformation of an OpenAPI specification file with jq. I want to 'recognise' the pattern

"status": {
  "allOf": [
    {
      "$ref": "#/components/schemas/Status"
    },
    {
      "description": "Status"
    }
  ]
}

and change it into

"status": {
  "$ref": "#/components/schemas/Status"
}

I don't think that is all that hard, but when I try to do it on a larger example, I run into trouble. Let's take as example:

  {
    "components": {
      "schemas": {
        "ObjectStatusType": {
          "required": [
            "status",
            "type",
            "agreement"
          ],
          "type": "object",
          "properties": {
            "agreement": {
              "description": "An agreement",
              "maxLength": 5,
              "type": "string"
            },
            "status": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/Status"
                },
                {
                  "description": "Status"
                }
              ]
            },
            "type": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/Type"
                },
                {
                  "description": "Type"
                }
              ]
            }
          }
        },
        "BallType": {
          "required": [
            "colour",
            "type",
            "physics"
          ],
          "type": "object",
          "properties": {
            "colour": {
              "description": "Ball colour",
              "maxLength": 5,
              "type": "string"
            },
            "type": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/Type"
                },
                {
                  "description": "Type"
                }
              ]
            },
            "physics": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/Physics"
                },
                {
                  "description": "Physics"
                }
              ]
            }
          }
        }
      }
    }
  }

which I'd like to turn into

  {
    "components": {
      "schemas": {
        "ObjectStatusType": {
          "required": [
            "status",
            "type",
            "agreement"
          ],
          "type": "object",
          "properties": {
            "agreement": {
              "description": "An agreement",
              "maxLength": 5,
              "type": "string"
            },
            "status": {
              "$ref": "#/components/schemas/Status"
            },
            "type": {
              "$ref": "#/components/schemas/Type"
            }
          }
        },
        "BallType": {
          "required": [
            "colour",
            "type",
            "physics"
          ],
          "type": "object",
          "properties": {
            "colour": {
              "description": "Ball colour",
              "maxLength": 5,
              "type": "string"
            },
            "type": {
              "$ref": "#/components/schemas/Type"
            },
            "physics": {
              "$ref": "#/components/schemas/Physics"
            }
          }
        }
      }
    }
  }

I've tried with the expression (.components | .schemas | .[] | .properties // empty | .[] | select(.allOf // empty | .[] // empty | .description) | select(.allOf // empty | .[] // empty | ."$ref")) = { "$ref" : "what to put here" } , which gives me

{
  "components": {
    "schemas": {
      "ObjectStatusType": {
        "required": [
          "status",
          "type",
          "agreement"
        ],
        "type": "object",
        "properties": {
          "agreement": {
            "description": "An agreement",
            "maxLength": 5,
            "type": "string"
          },
          "status": {
            "$ref": "what to put here"
          },
          "type": {
            "$ref": "what to put here"
          }
        }
      },
      "BallType": {
        "required": [
          "colour",
          "type",
          "physics"
        ],
        "type": "object",
        "properties": {
          "colour": {
            "description": "Ball colour",
            "maxLength": 5,
            "type": "string"
          },
          "type": {
            "$ref": "what to put here"
          },
          "physics": {
            "$ref": "what to put here"
          }
        }
      }
    }
  }
}

I need some reference to a 'subdocument' instead of what to put here. I've tried variables, but they can only be globally defined, and . on the right hand side refers to the entire document and not just the part being replaced.

We use YAML for the OpenAPI specification, so if there is a solution that also works with yq that would be preferable.

1 Answers

Given the input shown, the following jq program produces the expected output and seems to conform with the general requirements, and should be easy to modify according to your more detailed requirements:

walk(if type=="object" and (.allOf | (type == "array") and (length == 2))
     then {"$ref": .allOf[0]["$ref"]}
     else . end)

FYPI, the "then" clause can be abbreviated to:

.allOf[0] | {"$ref"}
Related