aws / aws-cli

Universal Command Line Interface for Amazon Web Services
Other
15.34k stars 4.09k forks source link

apigateway get-export does not include description or tags that were in the specification used to create the api #7633

Closed zomgbre closed 1 year ago

zomgbre commented 1 year ago

Describe the bug

When using an Open API 3.0.2 spec to create an API Gateway when using CDK construct SpecRestApi https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_apigateway.SpecRestApi.html, aws apigateway get-export does not include fields description and tags. There are probably others, but these are specifically important to me.

Expected Behavior

I expect that when I export the api spec it is much closer to what I uploaded to create the API.

Current Behavior

CDK Construct creation:

        const api = new SpecRestApi(this, 'Api', {
            apiDefinition: ApiDefinition.fromInline(definition.generate()),
            restApiName: `${definition.name}-${definition.version}`,
            deployOptions: {
                stageName: 'stage',
                metricsEnabled: true,
                tracingEnabled: true,
                accessLogDestination: new LogGroupLogDestination(logGroup)
            }
        });

This produces the following CloudFormation AWS::ApiGateway::RestApi (redacted some strings):

"ApiV1Api7B9A0327": {
   "Type": "AWS::ApiGateway::RestApi",
   "Properties": {
    "Body": {
     "openapi": "3.0.2",
     "info": {
      "title": "Search API",
      "description": "API for xxxx data.",
      "version": "v1"
     },
     "x-amazon-apigateway-api-key-source": "HEADER",
     "x-amazon-apigateway-request-validators": {
      "all": {
       "validateRequestBody": true,
       "validateRequestParameters": true
      }
     },
     "x-amazon-apigateway-request-validator": "all",
     "security": [
      {
       "api-key": []
      }
     ],
     "paths": {
      "/xxxx": {
       "get": {
        "tags": [
         "Search xxxx"
        ],
        "description": "Search for xxxx with the provided criteria.",
        "parameters": [
         {
          "$ref": "#/components/parameters/ppppId"
         },
         {
          "$ref": "#/components/parameters/search"
         },
         {
          "$ref": "#/components/parameters/offset"
         },
         {
          "$ref": "#/components/parameters/limit"
         },
         {
          "$ref": "#/components/parameters/locale"
         },
         {
          "$ref": "#/components/parameters/format"
         }
        ],
        "responses": {
         "200": {
          "description": "XXXX search response data.",
          "content": {
           "application/json": {
            "schema": {
             "$ref": "#/components/schemas/XXXXSearchResponse"
            }
           }
          }
         },
         "400": {
          "description": "Bad request for xxxx search.",
          "content": {
           "application/json": {
            "schema": {
             "$ref": "#/components/schemas/ErrorSearchResponse"
            }
           }
          }
         },
         "500": {
          "description": "Unexpected error on patients search.",
          "content": {
           "application/json": {
            "schema": {
             "$ref": "#/components/schemas/ErrorSearchResponse"
            }
           }
          }
         }
        },
        "x-amazon-apigateway-integration": {
         "responses": {
          "default": {
           "statusCode": "200"
          }
         },
         "requestTemplates": {
          "application/json": "{\"statusCode\": 200}"
         },
         "passthroughBehavior": "when_no_match",
         "type": "aws_proxy",
         "httpMethod": "POST",
         "uri": {
          "Fn::Join": [
           "",
           [
            "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/",
            {
             "Ref": "SearchXXXXAliascurrentB5EC10E8"
            },
            "/invocations"
           ]
          ]
         }
        }
       }
      },
      "/xxxx/{xxxxId}": {
       "get": {
        "tags": [
         "Suggest xxxx"
        ],
        "description": "Search for suggested xxxx for the provided xxxxId.",
        "parameters": [
         {
          "$ref": "#/components/parameters/ppppId"
         },
         {
          "$ref": "#/components/parameters/offset"
         },
         {
          "$ref": "#/components/parameters/limit"
         },
         {
          "$ref": "#/components/parameters/locale"
         },
         {
          "$ref": "#/components/parameters/format"
         }
        ],
        "responses": {
         "200": {
          "description": "Patients matching the provided patientId.",
          "content": {
           "application/json": {
            "schema": {
             "$ref": "#/components/schemas/PatientsSearchResponse"
            }
           }
          }
         },
         "400": {
          "description": "Bad request for xxxx search.",
          "content": {
           "application/json": {
            "schema": {
             "$ref": "#/components/schemas/ErrorSearchResponse"
            }
           }
          }
         },
         "404": {
          "description": "XXXX not found.",
          "content": {
           "application/json": {
            "schema": {
             "$ref": "#/components/schemas/ErrorSearchResponse"
            }
           }
          }
         },
         "500": {
          "description": "Unexpected error on xxxx search.",
          "content": {
           "application/json": {
            "schema": {
             "$ref": "#/components/schemas/ErrorSearchResponse"
            }
           }
          }
         }
        },
        "x-amazon-apigateway-integration": {
         "responses": {
          "default": {
           "statusCode": "200"
          }
         },
         "requestTemplates": {
          "application/json": "{\"statusCode\": 200}"
         },
         "passthroughBehavior": "when_no_match",
         "type": "aws_proxy",
         "httpMethod": "POST",
         "uri": {
          "Fn::Join": [
           "",
           [
            "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/",
            {
             "Ref": "SearchXXXXAliascurrentB5EC10E8"
            },
            "/invocations"
           ]
          ]
         }
        }
       }
      },
      "/yyyy": {
       "get": {
        "tags": [
         "Search YYYY"
        ],
        "description": "Search for yyyy with the provided criteria.",
        "parameters": [
         {
          "$ref": "#/components/parameters/ppppId"
         },
         {
          "$ref": "#/components/parameters/search"
         },
         {
          "$ref": "#/components/parameters/sort"
         },
         {
          "$ref": "#/components/parameters/offset"
         },
         {
          "$ref": "#/components/parameters/limit"
         },
         {
          "$ref": "#/components/parameters/locale"
         },
         {
          "$ref": "#/components/parameters/format"
         }
        ],
        "responses": {
         "200": {
          "description": "YYYY search response data.",
          "content": {
           "application/json": {
            "schema": {
             "$ref": "#/components/schemas/YYYYSearchResponse"
            }
           }
          }
         },
         "400": {
          "description": "Bad request for yyyy search.",
          "content": {
           "application/json": {
            "schema": {
             "$ref": "#/components/schemas/ErrorSearchResponse"
            }
           }
          }
         },
         "500": {
          "description": "Unexpected error on yyyy search.",
          "content": {
           "application/json": {
            "schema": {
             "$ref": "#/components/schemas/ErrorSearchResponse"
            }
           }
          }
         }
        },
        "x-amazon-apigateway-integration": {
         "responses": {
          "default": {
           "statusCode": "200"
          }
         },
         "requestTemplates": {
          "application/json": "{\"statusCode\": 200}"
         },
         "passthroughBehavior": "when_no_match",
         "type": "aws_proxy",
         "httpMethod": "POST",
         "uri": {
          "Fn::Join": [
           "",
           [
            "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/",
            {
             "Ref": "SearchYYYYAliascurrent77807FA2"
            },
            "/invocations"
           ]
          ]
         }
        }
       }
      }
     },
     "components": {
      "securitySchemes": {
       "api-key": {
        "type": "apiKey",
        "name": "x-api-key",
        "in": "header",
        "x-amazon-apigateway-api-key-source": "HEADER"
       }
      },
      "schemas": {
       "SearchMetadata": {
        "title": "SearchMetadata",
        "type": "object",
        "properties": {
         "returnedResults": {
          "type": "integer",
          "description": "Number of returned yyyy in the response."
         },
         "totalResults": {
          "type": "integer",
          "description": "Total number of matching yyyy."
         },
         "moreResults": {
          "type": "boolean",
          "description": "Boolean indicating if there are more results for pagination."
         },
         "offset": {
          "type": "integer",
          "description": "Zero-based pagination offset."
         },
         "offsetMax": {
          "type": "integer",
          "description": "Max page offset."
         },
         "limit": {
          "type": "integer",
          "description": "Limit of search results."
         },
         "suggestions": {
          "type": "array",
          "items": {
           "type": "string"
          },
          "description": "Search suggestions."
         }
        }
       },
       "XXXXSearchResponse": {
        "title": "XXXXSearchResponse",
        "type": "object",
        "properties": {
         "data": {
          "type": "array",
          "items": {
           "type": "object",
           "properties": {
            "id": {
             "type": "string"
            },
            "ppppId": {
             "type": "string"
            },
            "name": {
             "type": "string"
            }
           }
          }
         },
         "metadata": {
          "$ref": "#/components/schemas/SearchMetadata"
         }
        }
       },
       "YYYYSearchResponse": {
        "title": "YYYYSearchResponse",
        "type": "object",
        "properties": {
         "data": {
          "type": "array",
          "items": {
           "type": "object",
           "properties": {
            "id": {
             "type": "string"
            },
            "xxxx": {
             "type": "object",
             "properties": {
              "id": {
               "type": "string"
              },
              "ppppId": {
               "type": "string"
              },
              "name": {
               "type": "string"
              }
             }
            },
            "yyyy": {
             "type": "object",
             "properties": {
              "ppppId": {
               "type": "string"
              }
             }
            }
           }
          }
         },
         "metadata": {
          "$ref": "#/components/schemas/SearchMetadata"
         }
        }
       },
       "ErrorSearchResponse": {
        "title": "ErrorSearchResponse",
        "type": "object",
        "properties": {
         "error": {
          "type": "array",
          "items": {
           "type": "string"
          },
          "description": "Error detail messages."
         }
        }
       }
      },
      "parameters": {
       "ppppId": {
        "name": "ppppId",
        "in": "query",
        "description": "pppp ID to limit data by.",
        "schema": {
         "type": "string"
        }
       },
       "xxxxId": {
        "name": "xxxxId",
        "in": "query",
        "description": "xxxx ID to limit data by.",
        "schema": {
         "type": "string"
        }
       },
       "search": {
        "name": "search",
        "in": "query",
        "description": "Search query.",
        "schema": {
         "type": "string"
        }
       },
       "offset": {
        "name": "offset",
        "in": "query",
        "description": "Search results offset for pagination.",
        "schema": {
         "type": "integer"
        }
       },
       "limit": {
        "name": "limit",
        "in": "query",
        "description": "Search results limit.",
        "schema": {
         "type": "integer",
         "maximum": 500
        }
       },
       "locale": {
        "name": "locale",
        "in": "query",
        "description": "Search results locale code.",
        "schema": {
         "type": "string"
        }
       },
       "format": {
        "name": "format",
        "in": "query",
        "description": "Search results format.",
        "schema": {
         "type": "string",
         "enum": [
          "SEARCH",
          "IDS"
         ]
        }
       },
       "sort": {
        "name": "sort",
        "in": "query",
        "description": "Search results sort.",
        "schema": {
         "type": "object",
         "properties": {
          "asc": {
           "type": "string"
          },
          "desc": {
           "type": "string"
          }
         }
        },
        "style": "deepObject",
        "explode": true
       }
      }
     }
    },
    "Name": "rs-search-xxxx-xxxx-v1",
    "Tags": [
     {
      "Key": "Cost Center",
      "Value": "xxxx"
     },
     {
      "Key": "env",
      "Value": "xxxx"
     },
     {
      "Key": "Environment",
      "Value": "xxxx"
     },
     {
      "Key": "service",
      "Value": "xxxx-services-xxxx"
     }
    ]
   },
   "Metadata": {
    "aws:cdk:path": "xx-xxxx-xxxx/ApiV1/Api/Resource"
   }
  },

After deploying, running the following: aws apigateway get-export --rest-api-id xxxxx --stage-name stage --export-type oas30 spec-02-02-2023.json

Outputs (I tried my best to make it like the fields I censored hah):

{
    "openapi" : "3.0.1",
    "info" : {
      "title" : "xxxx-xxxx-xxxx-v1",
      "description" : "API for searching xxxx and yyyy data.",
      "version" : "v1"
    },
    "servers" : [ {
      "url" : "https://example.com/{basePath}",
      "variables" : {
        "basePath" : {
          "default" : "/v1/search"
        }
      }
    } ],
    "paths" : {
      "/xxxx" : {
        "get" : {
          "parameters" : [ {
            "name" : "ppppId",
            "in" : "query",
            "schema" : {
              "type" : "string"
            }
          }, {
            "name" : "search",
            "in" : "query",
            "schema" : {
              "type" : "string"
            }
          }, {
            "name" : "limit",
            "in" : "query",
            "schema" : {
              "type" : "string"
            }
          }, {
            "name" : "format",
            "in" : "query",
            "schema" : {
              "type" : "string"
            }
          }, {
            "name" : "offset",
            "in" : "query",
            "schema" : {
              "type" : "string"
            }
          }, {
            "name" : "locale",
            "in" : "query",
            "schema" : {
              "type" : "string"
            }
          }],
          "responses" : {
            "400" : {
              "description" : "400 response",
              "content" : {
                "application/json" : {
                  "schema" : {
                    "$ref" : "#/components/schemas/ErrorSearchResponse"
                  }
                }
              }
            },
            "500" : {
              "description" : "500 response",
              "content" : {
                "application/json" : {
                  "schema" : {
                    "$ref" : "#/components/schemas/ErrorSearchResponse"
                  }
                }
              }
            },
            "200" : {
              "description" : "200 response",
              "content" : {
                "application/json" : {
                  "schema" : {
                    "$ref" : "#/components/schemas/XXXXSearchResponse"
                  }
                }
              }
            }
          },
          "security" : [ {
            "api_key" : [ ]
          } ]
        }
      },
      "/yyyy" : {
        "get" : {
          "parameters" : [ {
            "name" : "xxxxId",
            "in" : "query",
            "schema" : {
              "type" : "string"
            }
          }, {
            "name" : "ppppId",
            "in" : "query",
            "schema" : {
              "type" : "string"
            }
          }, {
            "name" : "search",
            "in" : "query",
            "schema" : {
              "type" : "string"
            }
          }, {
            "name" : "sort",
            "in" : "query",
            "schema" : {
              "type" : "string"
            }
          }, {
            "name" : "limit",
            "in" : "query",
            "schema" : {
              "type" : "string"
            }
          }, {
            "name" : "format",
            "in" : "query",
            "schema" : {
              "type" : "string"
            }
          }, {
            "name" : "offset",
            "in" : "query",
            "schema" : {
              "type" : "string"
            }
          }, {
            "name" : "locale",
            "in" : "query",
            "schema" : {
              "type" : "string"
            }
          }],
          "responses" : {
            "400" : {
              "description" : "400 response",
              "content" : {
                "application/json" : {
                  "schema" : {
                    "$ref" : "#/components/schemas/ErrorSearchResponse"
                  }
                }
              }
            },
            "500" : {
              "description" : "500 response",
              "content" : {
                "application/json" : {
                  "schema" : {
                    "$ref" : "#/components/schemas/ErrorSearchResponse"
                  }
                }
              }
            },
            "200" : {
              "description" : "200 response",
              "content" : {
                "application/json" : {
                  "schema" : {
                    "$ref" : "#/components/schemas/YYYYSearchResponse"
                  }
                }
              }
            }
          },
          "security" : [ {
            "api_key" : [ ]
          } ]
        }
      },
      "/xxxx/{xxxxId}" : {
        "get" : {
          "parameters" : [ {
            "name" : "limit",
            "in" : "query",
            "schema" : {
              "type" : "string"
            }
          }, {
            "name" : "format",
            "in" : "query",
            "schema" : {
              "type" : "string"
            }
          }, {
            "name" : "ppppId",
            "in" : "query",
            "schema" : {
              "type" : "string"
            }
          }, {
            "name" : "offset",
            "in" : "query",
            "schema" : {
              "type" : "string"
            }
          }, {
            "name" : "xxxxId",
            "in" : "path",
            "required" : true,
            "schema" : {
              "type" : "string"
            }
          }, {
            "name" : "locale",
            "in" : "query",
            "schema" : {
              "type" : "string"
            }
          }],
          "responses" : {
            "404" : {
              "description" : "404 response",
              "content" : {
                "application/json" : {
                  "schema" : {
                    "$ref" : "#/components/schemas/ErrorSearchResponse"
                  }
                }
              }
            },
            "200" : {
              "description" : "200 response",
              "content" : {
                "application/json" : {
                  "schema" : {
                    "$ref" : "#/components/schemas/XXXXSearchResponse"
                  }
                }
              }
            },
            "400" : {
              "description" : "400 response",
              "content" : {
                "application/json" : {
                  "schema" : {
                    "$ref" : "#/components/schemas/ErrorSearchResponse"
                  }
                }
              }
            },
            "500" : {
              "description" : "500 response",
              "content" : {
                "application/json" : {
                  "schema" : {
                    "$ref" : "#/components/schemas/ErrorSearchResponse"
                  }
                }
              }
            }
          },
          "security" : [ {
            "api_key" : [ ]
          } ]
        }
      }
    },
    "components" : {
      "schemas" : {
        "ErrorSearchResponse" : {
          "title" : "ErrorSearchResponse",
          "type" : "object",
          "properties" : {
            "error" : {
              "type" : "array",
              "description" : "Error detail messages.",
              "items" : {
                "type" : "string"
              }
            }
          }
        },
        "XXXXSearchResponse" : {
          "title" : "XXXXSearchResponse",
          "type" : "object",
          "properties" : {
            "metadata" : {
              "$ref" : "#/components/schemas/SearchMetadata"
            },
            "data" : {
              "type" : "array",
              "items" : {
                "type" : "object",
                "properties" : {
                  "name" : {
                    "type" : "string"
                  },
                  "id" : {
                    "type" : "string"
                  }
                }
              }
            }
          }
        },
        "YYYYSearchResponse" : {
          "title" : "YYYYSearchResponse",
          "type" : "object",
          "properties" : {
            "metadata" : {
              "$ref" : "#/components/schemas/SearchMetadata"
            },
            "data" : {
              "type" : "array",
              "items" : {
                "type" : "object",
                "properties" : {
                  "result" : {
                    "type" : "object",
                    "properties" : {
                      "ppppId" : {
                        "type" : "string"
                      }
                    }
                  },
                  "xxxx" : {
                    "type" : "object",
                    "properties" : {
                      "ppppId" : {
                        "type" : "string"
                      },
                      "name" : {
                        "type" : "string"
                      },
                      "id" : {
                        "type" : "string"
                      }
                    }
                  },
                  "id" : {
                    "type" : "string"
                  }
                }
              }
            }
          }
        },
        "SearchMetadata" : {
          "title" : "SearchMetadata",
          "type" : "object",
          "properties" : {
            "totalResults" : {
              "type" : "integer",
              "description" : "Total number of matching search yyyy.",
              "format" : "int32"
            },
            "returnedResults" : {
              "type" : "integer",
              "description" : "Number of returned search results in the response.",
              "format" : "int32"
            },
            "offsetMax" : {
              "type" : "integer",
              "description" : "Max page offset.",
              "format" : "int32"
            },
            "offset" : {
              "type" : "integer",
              "description" : "Zero-based pagination offset.",
              "format" : "int32"
            },
            "limit" : {
              "type" : "integer",
              "description" : "Limit of search results.",
              "format" : "int32"
            },
            "suggestions" : {
              "type" : "array",
              "description" : "Search suggestions.",
              "items" : {
                "type" : "string"
              }
            },
            "moreResults" : {
              "type" : "boolean",
              "description" : "Boolean indicating if there are more results for pagination."
            }
          }
        }
      },
      "securitySchemes" : {
        "api_key" : {
          "type" : "apiKey",
          "name" : "x-api-key",
          "in" : "header"
        }
      }
    }
  }

As you can see, the output doesn't contain description or tags attribute under the paths. :sad

Reproduction Steps

See "current behavior".

  1. Create api gateway with an OAS 3.0.2 spec via CDK. Include descriptions under path.
  2. Deploy it somewhere!
  3. Export it out with the aws cli command aws apigateway get-export --rest-api-id xxxxx --stage-name stage --export-type oas30 spec-02-02-2023.json
  4. Observe no descriptions or tag attributes under paths.

Possible Solution

No response

Additional Information/Context

No response

CLI version used

aws-cli/2.2.25 Python/3.8.8 Darwin/21.6.0 exe/x86_64 prompt/off

Environment details (OS name and version, etc.)

macOS Monterey 12.5

zomgbre commented 1 year ago

Also, notice it doesn't preserve the type of the query parameters either... see limit and offset query parameters. It also is true for type boolean. I don't have it in the example, but it seems across the board there is some inconsistency.

tim-finnigan commented 1 year ago

Hi @zomgbre thanks for reaching out. Here is apigateway get-export documentation for reference: https://awscli.amazonaws.com/v2/documentation/api/latest/reference/apigateway/get-export.html.

It sounds like you are requesting changes to what this command returns, in which case this request should get rerouted to the API Gateway team for review since they own the underlying GetExport API. Could you alternatively use the GetTags API to get the tag/description info you need?

If the behavior you're describing doesn't match the documentation then adding --debug to your command and sharing the logs (with sensitive info redacted) would help with further investigation.

github-actions[bot] commented 1 year ago

Greetings! It looks like this issue hasn’t been active in longer than five days. We encourage you to check if this is still an issue in the latest release. In the absence of more information, we will be closing this issue soon. If you find that this is still a problem, please feel free to provide a comment or upvote with a reaction on the initial post to prevent automatic closure. If the issue is already closed, please feel free to open a new one.

ramon-navarro commented 6 months ago

Hi @zomgbre thanks for reaching out. Here is apigateway get-export documentation for reference: https://awscli.amazonaws.com/v2/documentation/api/latest/reference/apigateway/get-export.html.

It sounds like you are requesting changes to what this command returns, in which case this request should get rerouted to the API Gateway team for review since they own the underlying GetExport API. Could you alternatively use the GetTags API to get the tag/description info you need?

If the behavior you're describing doesn't match the documentation then adding --debug to your command and sharing the logs (with sensitive info redacted) would help with further investigation.

Hi @tim-finnigan, the issue is with tags of open api, not aws tags. it are different things.

the tags of open api are used to group resource in swagger ui, therefore are important in get-export

ramon-navarro commented 6 months ago

I have the same issue, any workaroud?