{
  "openapi": "3.1.1",
  "info": {
    "title": "PattyMenu.Api | v1",
    "description": "",
    "version": "1.0"
  },
  "servers": [
    {
      "url": "http://patty-api-staging.webion.it/"
    }
  ],
  "paths": {
    "/api/v1/menu/categories": {
      "get": {
        "tags": [
          "Menu"
        ],
        "summary": "Lists the categories of the menu with the number of items in each. Reads the menu page only,\nnever the detail pages.",
        "responses": {
          "200": {
            "description": "The categories.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ListMenuCategoriesResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListMenuCategoriesResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListMenuCategoriesResponse"
                }
              }
            }
          },
          "502": {
            "description": "pattyburger.it could not be read."
          }
        }
      }
    },
    "/api/v1/menu/{slug}": {
      "get": {
        "tags": [
          "Menu"
        ],
        "summary": "Reads a single item of the menu, always with its ingredients and allergens.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "description": "The item's identifier, for example `original-patty`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "refresh",
            "in": "query",
            "description": "Ignores the cached scrape and reads the site again.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The item.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/MenuItemDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MenuItemDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/MenuItemDto"
                }
              }
            }
          },
          "404": {
            "description": "The menu holds no item with that slug.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "502": {
            "description": "pattyburger.it could not be read."
          }
        }
      }
    },
    "/api/v1/menu": {
      "get": {
        "tags": [
          "Menu"
        ],
        "summary": "Reads the whole menu from pattyburger.it: name, photo and price of every item and, unless\nasked otherwise, the ingredients and allergens from each item's own page.",
        "parameters": [
          {
            "name": "includeDetails",
            "in": "query",
            "description": "Whether every item's detail page is read as well, which is what fills in details. Defaults to true; false answers from the menu page alone and is much faster.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "category",
            "in": "query",
            "description": "Returns only the items of this category, compared case-insensitively.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "refresh",
            "in": "query",
            "description": "Ignores the cached scrape and reads the site again.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The menu, in the order the site lists it.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/GetMenuResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetMenuResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetMenuResponse"
                }
              }
            }
          },
          "502": {
            "description": "pattyburger.it could not be read."
          }
        }
      }
    },
    "/api/v1/episodes": {
      "get": {
        "tags": [
          "Episodes"
        ],
        "summary": "Lists the episodes of \"Smash the rules\" from pattyburger.it: title, video and where the site\nlinks to watch it.",
        "parameters": [
          {
            "name": "refresh",
            "in": "query",
            "description": "Ignores the cached scrape and reads the site again.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The episodes.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ListEpisodesResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListEpisodesResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListEpisodesResponse"
                }
              }
            }
          },
          "502": {
            "description": "pattyburger.it could not be read."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AllergenDto": {
        "required": [
          "name"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "iconUrl": {
            "type": [
              "null",
              "string"
            ]
          }
        },
        "description": "An allergen contained in the product, with the icon the site draws for it."
      },
      "EpisodeDto": {
        "required": [
          "slug",
          "number",
          "title",
          "actionLabel"
        ],
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "description": "Stable identifier, built from the title — for example `dreaming`."
          },
          "number": {
            "type": "integer",
            "description": "Position in the series, starting at 1.",
            "format": "int32"
          },
          "title": {
            "type": "string",
            "description": "The title as the site writes it, in capitals — `DREAMING`."
          },
          "actionLabel": {
            "type": "string",
            "description": "The label on the site's button, for example `Guarda episodio 1`."
          },
          "videoUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "The video itself, an MP4 the site plays muted and looping. Null when the slide carries no\nplayable source."
          },
          "watchUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "Where the site's button goes — the episode on Instagram. Null when the slide has no link."
          }
        },
        "description": "One episode of \"Smash the rules\", the series the site plays on its about page."
      },
      "GetMenuResponse": {
        "required": [
          "source",
          "scrapedAt",
          "count",
          "items"
        ],
        "type": "object",
        "properties": {
          "source": {
            "type": "string",
            "description": "The page the menu was read from."
          },
          "scrapedAt": {
            "type": "string",
            "description": "When the site was last read. A response served from the cache keeps the timestamp of the\nread it came from, so it does not move until the cache expires.",
            "format": "date-time"
          },
          "count": {
            "type": "integer",
            "description": "How many items `items` holds, after the category filter.",
            "format": "int32"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MenuItemDto"
            }
          }
        }
      },
      "IngredientDto": {
        "required": [
          "name"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "iconUrl": {
            "type": [
              "null",
              "string"
            ]
          }
        },
        "description": "An ingredient, with the icon the site draws next to it."
      },
      "ListEpisodesResponse": {
        "required": [
          "source",
          "scrapedAt",
          "count",
          "episodes"
        ],
        "type": "object",
        "properties": {
          "source": {
            "type": "string",
            "description": "The page the episodes were read from."
          },
          "scrapedAt": {
            "type": "string",
            "description": "When the site was last read. A response served from the cache keeps the timestamp of the\nread it came from, so it does not move until the cache expires.",
            "format": "date-time"
          },
          "count": {
            "type": "integer",
            "format": "int32"
          },
          "episodes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EpisodeDto"
            },
            "description": "The episodes in the order the carousel plays them."
          }
        }
      },
      "ListMenuCategoriesResponse": {
        "required": [
          "categories"
        ],
        "type": "object",
        "properties": {
          "categories": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MenuCategoryDto"
            },
            "description": "The categories, in the order the menu page lists them."
          }
        }
      },
      "MenuCategoryDto": {
        "required": [
          "category",
          "count"
        ],
        "type": "object",
        "properties": {
          "category": {
            "type": "string"
          },
          "count": {
            "type": "integer",
            "format": "int32"
          }
        },
        "description": "A category of the menu and how many items it holds."
      },
      "MenuItemDetailsDto": {
        "required": [
          "descriptionParagraphs",
          "ingredients",
          "allergens"
        ],
        "type": "object",
        "properties": {
          "description": {
            "type": [
              "null",
              "string"
            ],
            "description": "The copy the site publishes for the item, as plain text. Paragraphs are separated by a blank\nline. Use it where the formatting does not matter; `descriptionParagraphs` carries the\nsame words with the emphasis the site gives them."
          },
          "descriptionParagraphs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TextParagraphDto"
            },
            "description": "The same copy, paragraph by paragraph, each split into runs that say whether the site sets\nthem bold, italic, in the brand colour or at another size."
          },
          "ingredients": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/IngredientDto"
            }
          },
          "allergens": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AllergenDto"
            },
            "description": "Only the allergens the product actually contains — the site's grid lists all fourteen and\nmarks the ones that apply."
          },
          "allergensNote": {
            "type": [
              "null",
              "string"
            ],
            "description": "The disclaimer the site prints above its allergen grid."
          }
        },
        "description": "What the item's own page adds to the menu listing."
      },
      "MenuItemDto": {
        "required": [
          "slug",
          "name",
          "category"
        ],
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "description": "Stable identifier, taken from the item's page on the site — for example `original-patty`."
          },
          "name": {
            "type": "string"
          },
          "category": {
            "type": "string",
            "description": "One of `Panini`, `Patatine`, `Dolci`, `Salse`."
          },
          "priceText": {
            "type": [
              "null",
              "string"
            ],
            "description": "The price exactly as printed on the site, for example `11,90€`.\nNull when the site shows no price for the item."
          },
          "price": {
            "type": [
              "null",
              "number"
            ],
            "description": "The same price as a number, null when `priceText` could not be parsed.",
            "format": "double"
          },
          "currency": {
            "type": "string"
          },
          "photoUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "Photo of the product, already an absolute URL."
          },
          "titleImageUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "The image the site uses to write the item's name, since the name itself is drawn, not typed."
          },
          "detailUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "The item's own page on pattyburger.it. Null for the sauces, which have none."
          },
          "details": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/MenuItemDetailsDto"
              }
            ],
            "description": "Description, ingredients and allergens. Null when the item has no detail page, when details\nwere not requested, or when its page could not be read."
          }
        },
        "description": "One entry of the menu: a burger, a side, a dessert or a sauce."
      },
      "ProblemDetails": {
        "type": "object",
        "properties": {
          "type": {
            "type": [
              "null",
              "string"
            ]
          },
          "title": {
            "type": [
              "null",
              "string"
            ]
          },
          "status": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "detail": {
            "type": [
              "null",
              "string"
            ]
          },
          "instance": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "TextParagraphDto": {
        "required": [
          "runs"
        ],
        "type": "object",
        "properties": {
          "runs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TextRunDto"
            }
          }
        },
        "description": "One paragraph of the description, split into the runs that are set differently."
      },
      "TextRunDto": {
        "required": [
          "text"
        ],
        "type": "object",
        "properties": {
          "text": {
            "type": "string",
            "description": "The text. A single line break inside a paragraph survives as `\\n`."
          },
          "bold": {
            "type": "boolean"
          },
          "italic": {
            "type": "boolean"
          },
          "accent": {
            "type": "boolean",
            "description": "The run is set in the brand colour rather than in the body colour."
          },
          "scale": {
            "type": [
              "null",
              "number"
            ],
            "description": "How much larger or smaller than the paragraph the run is set, `1` being the paragraph's\nown size. Null when the site does not resize it.",
            "format": "double"
          }
        },
        "description": "A stretch of text that is set the same way throughout."
      }
    }
  },
  "tags": [
    {
      "name": "Menu"
    },
    {
      "name": "Episodes"
    }
  ]
}