Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Semantic Error at OASv3 for 'MessageExcluding[id]': #3698

Closed
remkohdev opened this issue Sep 10, 2019 · 4 comments
Closed

Semantic Error at OASv3 for 'MessageExcluding[id]': #3698

remkohdev opened this issue Sep 10, 2019 · 4 comments
Labels

Comments

@remkohdev
Copy link

A Loopback4 generated application (https://github.com/remkohdev/guestbook101/blob/master/Lab0/README_w_lb4_cli.md) provides an OAS v3 that gives me the following semantic error in Swagger editor, which I assume uses Swagger Validator. I validated the Swagger in Swagger Editor after getting issues in API Connect (I now realize I should not be using v3 at all, when using API Connect, which means I cannot use Appsody, LB4 CLI, that's another story)

If I go to editor.swagger.io and import my OAS in Loopback4 (http://169.63.218.104:32145/openapi.yaml), the validator throws a semantic error 👍

Semantic Error at paths./messages.post.requestBody.content.application/json.schema.$ref
$ref values must be RFC3986-compliant percent-encoded URIs
Jump to line 190

Line 190 refers to $ref: '#/components/schemas/MessageExcluding[id]' that points to

     'MessageExcluding[id]':
     title: 'MessageExcluding[id]'
     not:
       anyOf:
         - required:
             - id
     properties:

I can resolve the error in Swagger Editor by URLEncoding the square brackets as follows: $ref: '#/components/schemas/MessageExcluding%5Bid%5D'

Does this mean the Loopback 4 application wrongly generates the ref? or is the error in the swagger validator in the swagger editor?

{
  "openapi": "3.0.0",
  "info": {
    "title": "LoopBack Application",
    "version": "1.0.0"
  },
  "paths": {
    "/addresses/{id}/contact": {
      "get": {
        "x-controller-name": "AddressContactController",
        "x-operation-name": "getContact",
        "tags": [
          "AddressContactController"
        ],
        "responses": {
          "200": {
            "description": "Contact belonging to Address",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Contact"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "number"
            },
            "required": true
          }
        ],
        "operationId": "AddressContactController.getContact"
      }
    },
    "/contacts/{id}/person": {
      "get": {
        "x-controller-name": "ContactPersonController",
        "x-operation-name": "getPerson",
        "tags": [
          "ContactPersonController"
        ],
        "responses": {
          "200": {
            "description": "Person belonging to Contact",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Person"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "number"
            },
            "required": true
          }
        ],
        "operationId": "ContactPersonController.getPerson"
      }
    },
    "/messages/count": {
      "get": {
        "x-controller-name": "MessagesController",
        "x-operation-name": "count",
        "tags": [
          "MessagesController"
        ],
        "responses": {
          "200": {
            "description": "Message model count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "where",
            "in": "query",
            "style": "deepObject",
            "explode": true,
            "schema": {
              "type": "object"
            }
          }
        ],
        "operationId": "MessagesController.count"
      }
    },
    "/messages/{id}/person": {
      "get": {
        "x-controller-name": "MessagePersonController",
        "x-operation-name": "getPerson",
        "tags": [
          "MessagePersonController"
        ],
        "responses": {
          "200": {
            "description": "Person belonging to Message",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Person"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "number"
            },
            "required": true
          }
        ],
        "operationId": "MessagePersonController.getPerson"
      }
    },
    "/messages/{id}": {
      "put": {
        "x-controller-name": "MessagesController",
        "x-operation-name": "replaceById",
        "tags": [
          "MessagesController"
        ],
        "responses": {
          "204": {
            "description": "Message PUT success"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "number"
            },
            "required": true
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Message"
              }
            }
          },
          "x-parameter-index": 1
        },
        "operationId": "MessagesController.replaceById"
      },
      "patch": {
        "x-controller-name": "MessagesController",
        "x-operation-name": "updateById",
        "tags": [
          "MessagesController"
        ],
        "responses": {
          "204": {
            "description": "Message PATCH success"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "number"
            },
            "required": true
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessagePartial"
              }
            }
          },
          "x-parameter-index": 1
        },
        "operationId": "MessagesController.updateById"
      },
      "get": {
        "x-controller-name": "MessagesController",
        "x-operation-name": "findById",
        "tags": [
          "MessagesController"
        ],
        "responses": {
          "200": {
            "description": "Message model instance",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "number"
            },
            "required": true
          }
        ],
        "operationId": "MessagesController.findById"
      },
      "delete": {
        "x-controller-name": "MessagesController",
        "x-operation-name": "deleteById",
        "tags": [
          "MessagesController"
        ],
        "responses": {
          "204": {
            "description": "Message DELETE success"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "number"
            },
            "required": true
          }
        ],
        "operationId": "MessagesController.deleteById"
      }
    },
    "/messages": {
      "post": {
        "x-controller-name": "MessagesController",
        "x-operation-name": "create",
        "tags": [
          "MessagesController"
        ],
        "responses": {
          "200": {
            "description": "Message model instance",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessageExcluding[id]"
              }
            }
          }
        },
        "operationId": "MessagesController.create"
      },
      "patch": {
        "x-controller-name": "MessagesController",
        "x-operation-name": "updateAll",
        "tags": [
          "MessagesController"
        ],
        "responses": {
          "200": {
            "description": "Message PATCH success count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "where",
            "in": "query",
            "style": "deepObject",
            "explode": true,
            "schema": {
              "type": "object"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessagePartial"
              }
            }
          }
        },
        "operationId": "MessagesController.updateAll"
      },
      "get": {
        "x-controller-name": "MessagesController",
        "x-operation-name": "find",
        "tags": [
          "MessagesController"
        ],
        "responses": {
          "200": {
            "description": "Array of Message model instances",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Message"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "filter",
            "in": "query",
            "style": "deepObject",
            "explode": true,
            "schema": {
              "type": "object",
              "properties": {
                "where": {
                  "type": "object"
                },
                "fields": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "boolean"
                    },
                    "text": {
                      "type": "boolean"
                    },
                    "datecreated": {
                      "type": "boolean"
                    },
                    "personId": {
                      "type": "boolean"
                    }
                  }
                },
                "offset": {
                  "type": "integer",
                  "minimum": 0
                },
                "limit": {
                  "type": "integer",
                  "minimum": 0
                },
                "skip": {
                  "type": "integer",
                  "minimum": 0
                },
                "order": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "include": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "relation": {
                        "type": "string"
                      },
                      "scope": {
                        "properties": {
                          "where": {
                            "type": "object"
                          },
                          "fields": {
                            "type": "object",
                            "properties": {}
                          },
                          "offset": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "limit": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "skip": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "order": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        ],
        "operationId": "MessagesController.find"
      }
    },
    "/ping": {
      "get": {
        "x-controller-name": "PingController",
        "x-operation-name": "ping",
        "tags": [
          "PingController"
        ],
        "responses": {
          "200": {
            "description": "Ping Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "greeting": {
                      "type": "string"
                    },
                    "date": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "headers": {
                      "type": "object",
                      "properties": {
                        "Content-Type": {
                          "type": "string"
                        }
                      },
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "PingController.ping"
      }
    }
  },
  "servers": [
    {
      "url": "http://169.63.218.104:32145"
    }
  ],
  "components": {
    "schemas": {
      "Contact": {
        "title": "Contact",
        "properties": {
          "id": {
            "type": "number"
          },
          "email": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "personId": {
            "type": "number"
          }
        }
      },
      "Person": {
        "title": "Person",
        "properties": {
          "id": {
            "type": "number"
          },
          "firstname": {
            "type": "string"
          },
          "lastname": {
            "type": "string"
          },
          "username": {
            "type": "string"
          }
        }
      },
      "Message": {
        "title": "Message",
        "properties": {
          "id": {
            "type": "number"
          },
          "text": {
            "type": "string"
          },
          "datecreated": {
            "type": "string",
            "format": "date-time"
          },
          "personId": {
            "type": "number"
          }
        },
        "required": [
          "text"
        ]
      },
      "MessageExcluding[id]": {
        "title": "MessageExcluding[id]",
        "not": {
          "anyOf": [
            {
              "required": [
                "id"
              ]
            }
          ]
        },
        "properties": {
          "text": {
            "type": "string"
          },
          "datecreated": {
            "type": "string",
            "format": "date-time"
          },
          "personId": {
            "type": "number"
          }
        },
        "required": [
          "text"
        ]
      },
      "MessagePartial": {
        "title": "MessagePartial",
        "properties": {
          "id": {
            "type": "number"
          },
          "text": {
            "type": "string"
          },
          "datecreated": {
            "type": "string",
            "format": "date-time"
          },
          "personId": {
            "type": "number"
          }
        }
      }
    }
  }
@remkohdev remkohdev added the bug label Sep 10, 2019
@dhmlau
Copy link
Member

dhmlau commented Sep 10, 2019

@jannyHou @raymondfeng , could you please help in this issue?
I was able to reproduce it using my sample app, so I don't think it's specific to a particular usage. Thanks.

@dhmlau
Copy link
Member

dhmlau commented Sep 10, 2019

@jannyHou, actually would it be fixed by your recent PR? #3667

@bajtos
Copy link
Member

bajtos commented Sep 10, 2019

Does this mean the Loopback 4 application wrongly generates the ref? or is the error in the swagger validator in the swagger editor?

Semantic Error at paths./messages.post.requestBody.content.application/json.schema.$ref
$ref values must be RFC3986-compliant percent-encoded URIs

I think this means the error is at LoopBack side, where we forgot to url-encode references.

I think the problem should go away once we land #3504, where we are changing the schema title to match the regular expression ^[a-zA-Z0-9.-_]+$

@dhmlau
Copy link
Member

dhmlau commented Oct 2, 2019

Since PR #3504 has landed, this issue should be resolved. Closing this.

@dhmlau dhmlau closed this as completed Oct 2, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants