From aab7ff14525be29a3aaa47a007644b826c8232e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=A4nel?= Date: Tue, 12 Aug 2025 17:29:02 +0200 Subject: [PATCH] docs: add OpenAPI documentation --- openapi/examples/AccountIn.yaml | 6 + openapi/examples/DipsOut.yaml | 45 + openapi/examples/HummusV1In.yaml | 15 + openapi/examples/HummusV1Out.yaml | 17 + openapi/examples/HummusV2In.yaml | 14 + openapi/examples/HummusV2Out.yaml | 16 + openapi/index.html | 24 + openapi/openapi-2025-02-13.schema.json | 1408 ++++++++++++++++++++++++ openapi/parameters/{recipeId}.yaml | 6 + openapi/paths/account-register.yaml | 23 + openapi/paths/recipe-api-bundled.yaml | 0 openapi/paths/recipe-new.yaml | 50 + openapi/paths/recipe-search.yaml | 59 + openapi/paths/recipe-{recipeId}.yaml | 117 ++ openapi/recipe-api-bundled.yaml | 497 +++++++++ openapi/recipe-api.yaml | 32 + openapi/redocly.yaml | 8 + openapi/schemas/Account.yaml | 9 + openapi/schemas/DateTime.yaml | 2 + openapi/schemas/Email.yaml | 3 + openapi/schemas/Id.yaml | 2 + openapi/schemas/NonBlankString.yaml | 2 + openapi/schemas/Password.yaml | 4 + openapi/schemas/ProblemDetail.yaml | 24 + openapi/schemas/Recipe.yaml | 39 + 25 files changed, 2422 insertions(+) create mode 100644 openapi/examples/AccountIn.yaml create mode 100644 openapi/examples/DipsOut.yaml create mode 100644 openapi/examples/HummusV1In.yaml create mode 100644 openapi/examples/HummusV1Out.yaml create mode 100644 openapi/examples/HummusV2In.yaml create mode 100644 openapi/examples/HummusV2Out.yaml create mode 100644 openapi/index.html create mode 100644 openapi/openapi-2025-02-13.schema.json create mode 100644 openapi/parameters/{recipeId}.yaml create mode 100644 openapi/paths/account-register.yaml create mode 100644 openapi/paths/recipe-api-bundled.yaml create mode 100644 openapi/paths/recipe-new.yaml create mode 100644 openapi/paths/recipe-search.yaml create mode 100644 openapi/paths/recipe-{recipeId}.yaml create mode 100644 openapi/recipe-api-bundled.yaml create mode 100644 openapi/recipe-api.yaml create mode 100644 openapi/redocly.yaml create mode 100644 openapi/schemas/Account.yaml create mode 100644 openapi/schemas/DateTime.yaml create mode 100644 openapi/schemas/Email.yaml create mode 100644 openapi/schemas/Id.yaml create mode 100644 openapi/schemas/NonBlankString.yaml create mode 100644 openapi/schemas/Password.yaml create mode 100644 openapi/schemas/ProblemDetail.yaml create mode 100644 openapi/schemas/Recipe.yaml diff --git a/openapi/examples/AccountIn.yaml b/openapi/examples/AccountIn.yaml new file mode 100644 index 0000000..87ac7f8 --- /dev/null +++ b/openapi/examples/AccountIn.yaml @@ -0,0 +1,6 @@ +summary: Account registration +value: + { + "email": "ada@example.com", + "password": "FBR5h0L1ENA3edctgDIEnxSvFdR0", + } \ No newline at end of file diff --git a/openapi/examples/DipsOut.yaml b/openapi/examples/DipsOut.yaml new file mode 100644 index 0000000..c21332b --- /dev/null +++ b/openapi/examples/DipsOut.yaml @@ -0,0 +1,45 @@ +summary: Dip category search +value: + [ + { + "id": 3, + "name": "Muhammara", + "description": "Muhammara is a vibrant and flavorful dip or spread from the Middle East, particularly popular in Syrian and Turkish cuisine. It's made primarily from roasted red bell peppers, walnuts, breadcrumbs, and pomegranate molasses.", + "changedAt": "2025-08-08T16:24:44.673034Z", + "categories": [ "Dip" ], + "ingredients": + [ + "Pomegranate molasses", + "Red bell peppers", + "Bread crumbs", + "Cumin", + "Chili flakes", + "Garlic", + "Walnuts", + "Lemon juice", + ], + "steps": + [ + "Roast whole bell peppers in an oven or air fryer and wait until blackened on the outside.", + "Put the peppers in a bowl cover them with to let steam and cool down", + "Remove core seeds, and skins", + "Combine all the ingredients in a food processor.", + "Process until mostly smooth but with a little texture remaining.", + "Taste and adjust flavor as needed (lemon for acidity, pomegranate molasses for sweetness)", + ], + }, + { + "id": 2, + "name": "Hummus", + "description": "Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic.", + "changedAt": "2025-08-08T16:24:44.579213Z", + "categories": [ "Dip" ], + "ingredients": [ "Chickpeas", "Tahini", "Lemon juice", "Garlic" ], + "steps": + [ + "Combine chickpeas, tahini, lemon juice, garlic, and salt in a food processor.", + "Blend until smooth, slowly adding cold water until you reach a creamy consistency.", + "Taste and adjust seasoning if needed.", + ], + }, + ] diff --git a/openapi/examples/HummusV1In.yaml b/openapi/examples/HummusV1In.yaml new file mode 100644 index 0000000..6b0b13a --- /dev/null +++ b/openapi/examples/HummusV1In.yaml @@ -0,0 +1,15 @@ +summary: Hummus recipe +value: + { + "name": "Hummus", + "description": "Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic.", + "categories": [ "Dip", "Spread" ], + "ingredients": + [ "Chickpeas", "Tahini", "Olive oil", "Lemon juice", "Garlic" ], + "steps": + [ + "Combine chickpeas, tahini, olive oil, lemon juice, garlic, and salt in a food processor.", + "Blend until smooth, slowly adding cold water until you reach a creamy consistency.", + "Taste and adjust seasoning if needed.", + ], + } diff --git a/openapi/examples/HummusV1Out.yaml b/openapi/examples/HummusV1Out.yaml new file mode 100644 index 0000000..e94ef09 --- /dev/null +++ b/openapi/examples/HummusV1Out.yaml @@ -0,0 +1,17 @@ +summary: Hummus recipe +value: + { + "id": 1729, + "name": "Hummus", + "description": "Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic.", + "changedAt": "2025-08-08T15:35:10.294411Z", + "categories": [ "Dip", "Spread" ], + "ingredients": + [ "Chickpeas", "Tahini", "Olive oil", "Lemon juice", "Garlic" ], + "steps": + [ + "Combine chickpeas, tahini, olive oil, lemon juice, garlic, and salt in a food processor.", + "Blend until smooth, slowly adding cold water until you reach a creamy consistency.", + "Taste and adjust seasoning if needed.", + ], + } diff --git a/openapi/examples/HummusV2In.yaml b/openapi/examples/HummusV2In.yaml new file mode 100644 index 0000000..59ad8d8 --- /dev/null +++ b/openapi/examples/HummusV2In.yaml @@ -0,0 +1,14 @@ +summary: Hummus recipe update +value: + { + "name": "Hummus", + "description": "Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic.", + "categories": [ "Dip", "Spread" ], + "ingredients": [ "Chickpeas", "Tahini", "Lemon juice", "Garlic" ], + "steps": + [ + "Combine chickpeas, tahini, lemon juice, garlic, and salt in a food processor.", + "Blend until smooth, slowly adding cold water until you reach a creamy consistency.", + "Taste and adjust seasoning if needed.", + ], + } diff --git a/openapi/examples/HummusV2Out.yaml b/openapi/examples/HummusV2Out.yaml new file mode 100644 index 0000000..56959e0 --- /dev/null +++ b/openapi/examples/HummusV2Out.yaml @@ -0,0 +1,16 @@ +summary: Hummus recipe update +value: + { + "id": 1729, + "name": "Hummus", + "description": "Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic.", + "changedAt": "2025-08-09T12:48:32.134821Z", + "categories": [ "Dip", "Spread" ], + "ingredients": [ "Chickpeas", "Tahini", "Lemon juice", "Garlic" ], + "steps": + [ + "Combine chickpeas, tahini, lemon juice, garlic, and salt in a food processor.", + "Blend until smooth, slowly adding cold water until you reach a creamy consistency.", + "Taste and adjust seasoning if needed.", + ], + } diff --git a/openapi/index.html b/openapi/index.html new file mode 100644 index 0000000..530e213 --- /dev/null +++ b/openapi/index.html @@ -0,0 +1,24 @@ + + + + Recipe API Reference + + + + + +
+ + + + + + + + diff --git a/openapi/openapi-2025-02-13.schema.json b/openapi/openapi-2025-02-13.schema.json new file mode 100644 index 0000000..acaedec --- /dev/null +++ b/openapi/openapi-2025-02-13.schema.json @@ -0,0 +1,1408 @@ +{ + "$id": "https://spec.openapis.org/oas/3.1/schema/2025-02-13", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The description of OpenAPI v3.1.x Documents without Schema Object validation", + "type": "object", + "properties": { + "openapi": { + "type": "string", + "pattern": "^3\\.1\\.\\d+(-.+)?$" + }, + "info": { + "$ref": "#/$defs/info" + }, + "jsonSchemaDialect": { + "type": "string", + "format": "uri-reference", + "default": "https://spec.openapis.org/oas/3.1/dialect/2024-11-10" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + }, + "default": [ + { + "url": "/" + } + ] + }, + "paths": { + "$ref": "#/$defs/paths" + }, + "webhooks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/path-item" + } + }, + "components": { + "$ref": "#/$defs/components" + }, + "security": { + "type": "array", + "items": { + "$ref": "#/$defs/security-requirement" + } + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/$defs/tag" + } + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + } + }, + "required": [ + "openapi", + "info" + ], + "anyOf": [ + { + "required": [ + "paths" + ] + }, + { + "required": [ + "components" + ] + }, + { + "required": [ + "webhooks" + ] + } + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "$defs": { + "info": { + "$comment": "https://spec.openapis.org/oas/v3.1#info-object", + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "termsOfService": { + "type": "string", + "format": "uri-reference" + }, + "contact": { + "$ref": "#/$defs/contact" + }, + "license": { + "$ref": "#/$defs/license" + }, + "version": { + "type": "string" + } + }, + "required": [ + "title", + "version" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "contact": { + "$comment": "https://spec.openapis.org/oas/v3.1#contact-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri-reference" + }, + "email": { + "type": "string", + "format": "email" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "license": { + "$comment": "https://spec.openapis.org/oas/v3.1#license-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "identifier": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri-reference" + } + }, + "required": [ + "name" + ], + "dependentSchemas": { + "identifier": { + "not": { + "required": [ + "url" + ] + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "server": { + "$comment": "https://spec.openapis.org/oas/v3.1#server-object", + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "variables": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/server-variable" + } + } + }, + "required": [ + "url" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "server-variable": { + "$comment": "https://spec.openapis.org/oas/v3.1#server-variable-object", + "type": "object", + "properties": { + "enum": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "default": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "default" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "components": { + "$comment": "https://spec.openapis.org/oas/v3.1#components-object", + "type": "object", + "properties": { + "schemas": { + "type": "object", + "additionalProperties": { + "$dynamicRef": "#meta" + } + }, + "responses": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/response-or-reference" + } + }, + "parameters": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "examples": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/example-or-reference" + } + }, + "requestBodies": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/request-body-or-reference" + } + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "securitySchemes": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/security-scheme-or-reference" + } + }, + "links": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/link-or-reference" + } + }, + "callbacks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/callbacks-or-reference" + } + }, + "pathItems": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/path-item" + } + } + }, + "patternProperties": { + "^(schemas|responses|parameters|examples|requestBodies|headers|securitySchemes|links|callbacks|pathItems)$": { + "$comment": "Enumerating all of the property names in the regex above is necessary for unevaluatedProperties to work as expected", + "propertyNames": { + "pattern": "^[a-zA-Z0-9._-]+$" + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "paths": { + "$comment": "https://spec.openapis.org/oas/v3.1#paths-object", + "type": "object", + "patternProperties": { + "^/": { + "$ref": "#/$defs/path-item" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "path-item": { + "$comment": "https://spec.openapis.org/oas/v3.1#path-item-object", + "type": "object", + "properties": { + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + } + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "get": { + "$ref": "#/$defs/operation" + }, + "put": { + "$ref": "#/$defs/operation" + }, + "post": { + "$ref": "#/$defs/operation" + }, + "delete": { + "$ref": "#/$defs/operation" + }, + "options": { + "$ref": "#/$defs/operation" + }, + "head": { + "$ref": "#/$defs/operation" + }, + "patch": { + "$ref": "#/$defs/operation" + }, + "trace": { + "$ref": "#/$defs/operation" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "operation": { + "$comment": "https://spec.openapis.org/oas/v3.1#operation-object", + "type": "object", + "properties": { + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + }, + "operationId": { + "type": "string" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "requestBody": { + "$ref": "#/$defs/request-body-or-reference" + }, + "responses": { + "$ref": "#/$defs/responses" + }, + "callbacks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/callbacks-or-reference" + } + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "security": { + "type": "array", + "items": { + "$ref": "#/$defs/security-requirement" + } + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "external-documentation": { + "$comment": "https://spec.openapis.org/oas/v3.1#external-documentation-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri-reference" + } + }, + "required": [ + "url" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "parameter": { + "$comment": "https://spec.openapis.org/oas/v3.1#parameter-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "in": { + "enum": [ + "query", + "header", + "path", + "cookie" + ] + }, + "description": { + "type": "string" + }, + "required": { + "default": false, + "type": "boolean" + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "schema": { + "$dynamicRef": "#meta" + }, + "content": { + "$ref": "#/$defs/content", + "minProperties": 1, + "maxProperties": 1 + } + }, + "required": [ + "name", + "in" + ], + "oneOf": [ + { + "required": [ + "schema" + ] + }, + { + "required": [ + "content" + ] + } + ], + "if": { + "properties": { + "in": { + "const": "query" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "allowEmptyValue": { + "default": false, + "type": "boolean" + } + } + }, + "dependentSchemas": { + "schema": { + "properties": { + "style": { + "type": "string" + }, + "explode": { + "type": "boolean" + } + }, + "allOf": [ + { + "$ref": "#/$defs/examples" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-path" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-header" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-query" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-cookie" + }, + { + "$ref": "#/$defs/styles-for-form" + } + ], + "$defs": { + "styles-for-path": { + "if": { + "properties": { + "in": { + "const": "path" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "simple", + "enum": [ + "matrix", + "label", + "simple" + ] + }, + "required": { + "const": true + } + }, + "required": [ + "required" + ] + } + }, + "styles-for-header": { + "if": { + "properties": { + "in": { + "const": "header" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "simple", + "const": "simple" + } + } + } + }, + "styles-for-query": { + "if": { + "properties": { + "in": { + "const": "query" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "form", + "enum": [ + "form", + "spaceDelimited", + "pipeDelimited", + "deepObject" + ] + }, + "allowReserved": { + "default": false, + "type": "boolean" + } + } + } + }, + "styles-for-cookie": { + "if": { + "properties": { + "in": { + "const": "cookie" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "form", + "const": "form" + } + } + } + } + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "parameter-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/parameter" + } + }, + "request-body": { + "$comment": "https://spec.openapis.org/oas/v3.1#request-body-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "content": { + "$ref": "#/$defs/content" + }, + "required": { + "default": false, + "type": "boolean" + } + }, + "required": [ + "content" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "request-body-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/request-body" + } + }, + "content": { + "$comment": "https://spec.openapis.org/oas/v3.1#fixed-fields-10", + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/media-type" + }, + "propertyNames": { + "format": "media-range" + } + }, + "media-type": { + "$comment": "https://spec.openapis.org/oas/v3.1#media-type-object", + "type": "object", + "properties": { + "schema": { + "$dynamicRef": "#meta" + }, + "encoding": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/encoding" + } + } + }, + "allOf": [ + { + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/examples" + } + ], + "unevaluatedProperties": false + }, + "encoding": { + "$comment": "https://spec.openapis.org/oas/v3.1#encoding-object", + "type": "object", + "properties": { + "contentType": { + "type": "string", + "format": "media-range" + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "style": { + "default": "form", + "enum": [ + "form", + "spaceDelimited", + "pipeDelimited", + "deepObject" + ] + }, + "explode": { + "type": "boolean" + }, + "allowReserved": { + "default": false, + "type": "boolean" + } + }, + "allOf": [ + { + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/styles-for-form" + } + ], + "unevaluatedProperties": false + }, + "responses": { + "$comment": "https://spec.openapis.org/oas/v3.1#responses-object", + "type": "object", + "properties": { + "default": { + "$ref": "#/$defs/response-or-reference" + } + }, + "patternProperties": { + "^[1-5](?:[0-9]{2}|XX)$": { + "$ref": "#/$defs/response-or-reference" + } + }, + "minProperties": 1, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "if": { + "$comment": "either default, or at least one response code property must exist", + "patternProperties": { + "^[1-5](?:[0-9]{2}|XX)$": false + } + }, + "then": { + "required": [ + "default" + ] + } + }, + "response": { + "$comment": "https://spec.openapis.org/oas/v3.1#response-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "content": { + "$ref": "#/$defs/content" + }, + "links": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/link-or-reference" + } + } + }, + "required": [ + "description" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "response-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/response" + } + }, + "callbacks": { + "$comment": "https://spec.openapis.org/oas/v3.1#callback-object", + "type": "object", + "$ref": "#/$defs/specification-extensions", + "additionalProperties": { + "$ref": "#/$defs/path-item" + } + }, + "callbacks-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/callbacks" + } + }, + "example": { + "$comment": "https://spec.openapis.org/oas/v3.1#example-object", + "type": "object", + "properties": { + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "value": true, + "externalValue": { + "type": "string", + "format": "uri-reference" + } + }, + "not": { + "required": [ + "value", + "externalValue" + ] + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "example-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/example" + } + }, + "link": { + "$comment": "https://spec.openapis.org/oas/v3.1#link-object", + "type": "object", + "properties": { + "operationRef": { + "type": "string", + "format": "uri-reference" + }, + "operationId": { + "type": "string" + }, + "parameters": { + "$ref": "#/$defs/map-of-strings" + }, + "requestBody": true, + "description": { + "type": "string" + }, + "body": { + "$ref": "#/$defs/server" + } + }, + "oneOf": [ + { + "required": [ + "operationRef" + ] + }, + { + "required": [ + "operationId" + ] + } + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "link-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/link" + } + }, + "header": { + "$comment": "https://spec.openapis.org/oas/v3.1#header-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "required": { + "default": false, + "type": "boolean" + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "schema": { + "$dynamicRef": "#meta" + }, + "content": { + "$ref": "#/$defs/content", + "minProperties": 1, + "maxProperties": 1 + } + }, + "oneOf": [ + { + "required": [ + "schema" + ] + }, + { + "required": [ + "content" + ] + } + ], + "dependentSchemas": { + "schema": { + "properties": { + "style": { + "default": "simple", + "const": "simple" + }, + "explode": { + "default": false, + "type": "boolean" + } + }, + "$ref": "#/$defs/examples" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "header-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/header" + } + }, + "tag": { + "$comment": "https://spec.openapis.org/oas/v3.1#tag-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + } + }, + "required": [ + "name" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "reference": { + "$comment": "https://spec.openapis.org/oas/v3.1#reference-object", + "type": "object", + "properties": { + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + } + } + }, + "schema": { + "$comment": "https://spec.openapis.org/oas/v3.1#schema-object", + "$dynamicAnchor": "meta", + "type": [ + "object", + "boolean" + ] + }, + "security-scheme": { + "$comment": "https://spec.openapis.org/oas/v3.1#security-scheme-object", + "type": "object", + "properties": { + "type": { + "enum": [ + "apiKey", + "http", + "mutualTLS", + "oauth2", + "openIdConnect" + ] + }, + "description": { + "type": "string" + } + }, + "required": [ + "type" + ], + "allOf": [ + { + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-apikey" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-http" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-http-bearer" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-oauth2" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-oidc" + } + ], + "unevaluatedProperties": false, + "$defs": { + "type-apikey": { + "if": { + "properties": { + "type": { + "const": "apiKey" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "name": { + "type": "string" + }, + "in": { + "enum": [ + "query", + "header", + "cookie" + ] + } + }, + "required": [ + "name", + "in" + ] + } + }, + "type-http": { + "if": { + "properties": { + "type": { + "const": "http" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "scheme": { + "type": "string" + } + }, + "required": [ + "scheme" + ] + } + }, + "type-http-bearer": { + "if": { + "properties": { + "type": { + "const": "http" + }, + "scheme": { + "type": "string", + "pattern": "^[Bb][Ee][Aa][Rr][Ee][Rr]$" + } + }, + "required": [ + "type", + "scheme" + ] + }, + "then": { + "properties": { + "bearerFormat": { + "type": "string" + } + } + } + }, + "type-oauth2": { + "if": { + "properties": { + "type": { + "const": "oauth2" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "flows": { + "$ref": "#/$defs/oauth-flows" + } + }, + "required": [ + "flows" + ] + } + }, + "type-oidc": { + "if": { + "properties": { + "type": { + "const": "openIdConnect" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "openIdConnectUrl": { + "type": "string", + "format": "uri-reference" + } + }, + "required": [ + "openIdConnectUrl" + ] + } + } + } + }, + "security-scheme-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/security-scheme" + } + }, + "oauth-flows": { + "type": "object", + "properties": { + "implicit": { + "$ref": "#/$defs/oauth-flows/$defs/implicit" + }, + "password": { + "$ref": "#/$defs/oauth-flows/$defs/password" + }, + "clientCredentials": { + "$ref": "#/$defs/oauth-flows/$defs/client-credentials" + }, + "authorizationCode": { + "$ref": "#/$defs/oauth-flows/$defs/authorization-code" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "$defs": { + "implicit": { + "type": "object", + "properties": { + "authorizationUrl": { + "type": "string", + "format": "uri-reference" + }, + "refreshUrl": { + "type": "string", + "format": "uri-reference" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "authorizationUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "password": { + "type": "object", + "properties": { + "tokenUrl": { + "type": "string", + "format": "uri-reference" + }, + "refreshUrl": { + "type": "string", + "format": "uri-reference" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "client-credentials": { + "type": "object", + "properties": { + "tokenUrl": { + "type": "string", + "format": "uri-reference" + }, + "refreshUrl": { + "type": "string", + "format": "uri-reference" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "authorization-code": { + "type": "object", + "properties": { + "authorizationUrl": { + "type": "string", + "format": "uri-reference" + }, + "tokenUrl": { + "type": "string", + "format": "uri-reference" + }, + "refreshUrl": { + "type": "string", + "format": "uri-reference" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "authorizationUrl", + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + } + } + }, + "security-requirement": { + "$comment": "https://spec.openapis.org/oas/v3.1#security-requirement-object", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "specification-extensions": { + "$comment": "https://spec.openapis.org/oas/v3.1#specification-extensions", + "patternProperties": { + "^x-": true + } + }, + "examples": { + "properties": { + "example": true, + "examples": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/example-or-reference" + } + } + } + }, + "map-of-strings": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "styles-for-form": { + "if": { + "properties": { + "style": { + "const": "form" + } + }, + "required": [ + "style" + ] + }, + "then": { + "properties": { + "explode": { + "default": true + } + } + }, + "else": { + "properties": { + "explode": { + "default": false + } + } + } + } + } +} diff --git a/openapi/parameters/{recipeId}.yaml b/openapi/parameters/{recipeId}.yaml new file mode 100644 index 0000000..6d0a6a2 --- /dev/null +++ b/openapi/parameters/{recipeId}.yaml @@ -0,0 +1,6 @@ +name: "recipeId" +in: "path" +required: true +schema: + $ref: "../schemas/Id.yaml" +description: "Unique identifier of the recipe" diff --git a/openapi/paths/account-register.yaml b/openapi/paths/account-register.yaml new file mode 100644 index 0000000..be8cb03 --- /dev/null +++ b/openapi/paths/account-register.yaml @@ -0,0 +1,23 @@ +post: + tags: + - Account + summary: "Register an account" + operationId: "RegisterAccount" + requestBody: + content: + application/json: + schema: + $ref: "../schemas/Account.yaml" + examples: + accountIn: + $ref: "../examples/AccountIn.yaml" + required: true + responses: + "200": + description: "OK" + "400": + description: "Bad Request" + content: + "application/json": + schema: + $ref: "../schemas/ProblemDetail.yaml" diff --git a/openapi/paths/recipe-api-bundled.yaml b/openapi/paths/recipe-api-bundled.yaml new file mode 100644 index 0000000..e69de29 diff --git a/openapi/paths/recipe-new.yaml b/openapi/paths/recipe-new.yaml new file mode 100644 index 0000000..a0ef32b --- /dev/null +++ b/openapi/paths/recipe-new.yaml @@ -0,0 +1,50 @@ +post: + tags: + - Recipe + summary: "Add a recipe" + operationId: "addRecipe" + requestBody: + content: + application/json: + schema: + $ref: "../schemas/Recipe.yaml" + examples: + recipeIn: + $ref: "../examples/HummusV1In.yaml" + required: true + responses: + 200: + description: OK + content: + "application/json": + schema: + $ref: "../schemas/Recipe.yaml" + examples: + recipeOut: + $ref: "../examples/HummusV1Out.yaml" + 400: + description: Bad Request + content: + "application/json": + schema: + $ref: "../schemas/ProblemDetail.yaml" + 401: + description: Unauthorized + content: + "application/json": + schema: + $ref: "../schemas/ProblemDetail.yaml" + 403: + description: Forbidden + content: + "application/json": + schema: + $ref: "../schemas/ProblemDetail.yaml" + 404: + description: Not Found + content: + "application/json": + schema: + $ref: "../schemas/ProblemDetail.yaml" + security: + - httpBasic: [ ] diff --git a/openapi/paths/recipe-search.yaml b/openapi/paths/recipe-search.yaml new file mode 100644 index 0000000..ea36024 --- /dev/null +++ b/openapi/paths/recipe-search.yaml @@ -0,0 +1,59 @@ +get: + tags: + - Recipe + summary: "Find recipes" + operationId: "FindRecipes" + parameters: + - name: "category" + in: "query" + required: false + schema: + type: "string" + description: "Category name filter (equals, ignores case)" + - name: "recipe" + in: "query" + required: false + schema: + type: "string" + description: "Recipe name filter (contains, ignores case)" + - name: "limit" + in: "query" + required: true + schema: + type: "integer" + format: "int64" + minimum: 1 + maximum: 100 + description: "Maximum number of recipes to return" + - name: "offset" + in: "query" + required: true + schema: + type: "integer" + format: "int64" + minimum: 0 + description: "Index into result set from where recipes will start to be returned" + responses: + 200: + description: OK + content: + "application/json": + schema: + type: "array" + items: + $ref: "../schemas/Recipe.yaml" + examples: + dipsOut: + $ref: "../examples/DipsOut.yaml" + 400: + description: Bad Request + content: + "application/json": + schema: + $ref: "../schemas/ProblemDetail.yaml" + 404: + description: Not Found + content: + "application/json": + schema: + $ref: "../schemas/ProblemDetail.yaml" diff --git a/openapi/paths/recipe-{recipeId}.yaml b/openapi/paths/recipe-{recipeId}.yaml new file mode 100644 index 0000000..b513833 --- /dev/null +++ b/openapi/paths/recipe-{recipeId}.yaml @@ -0,0 +1,117 @@ +get: + tags: + - Recipe + summary: "Get a recipe" + operationId: "GetRecipe" + parameters: + - $ref: "../parameters/{recipeId}.yaml" + responses: + 200: + description: OK + content: + "application/json": + schema: + $ref: "../schemas/Recipe.yaml" + examples: + recipeOut: + $ref: "../examples/HummusV1Out.yaml" + 400: + description: Bad Request + content: + "application/json": + schema: + $ref: "../schemas/ProblemDetail.yaml" + 404: + description: Not Found + content: + "application/json": + schema: + $ref: "../schemas/ProblemDetail.yaml" +put: + tags: + - Recipe + summary: "Update a recipe" + operationId: "UpdateRecipe" + parameters: + - $ref: "../parameters/{recipeId}.yaml" + requestBody: + content: + application/json: + schema: + $ref: "../schemas/Recipe.yaml" + examples: + recipeIn: + $ref: "../examples/HummusV2In.yaml" + required: true + responses: + 200: + description: OK + content: + "application/json": + schema: + $ref: "../schemas/Recipe.yaml" + examples: + recipeOut: + $ref: "../examples/HummusV2Out.yaml" + 400: + description: Bad Request + content: + "application/json": + schema: + $ref: "../schemas/ProblemDetail.yaml" + 401: + description: Unauthorized + content: + "application/json": + schema: + $ref: "../schemas/ProblemDetail.yaml" + 403: + description: Forbidden + content: + "application/json": + schema: + $ref: "../schemas/ProblemDetail.yaml" + 404: + description: Not Found + content: + "application/json": + schema: + $ref: "../schemas/ProblemDetail.yaml" + security: + - httpBasic: [ ] +delete: + tags: + - Recipe + summary: "Remove a recipe" + operationId: "RemoveRecipe" + parameters: + - $ref: "../parameters/{recipeId}.yaml" + responses: + 200: + description: OK + 400: + description: Bad Request + content: + "application/json": + schema: + $ref: "../schemas/ProblemDetail.yaml" + 401: + description: Unauthorized + content: + "application/json": + schema: + $ref: "../schemas/ProblemDetail.yaml" + 403: + description: Forbidden + content: + "application/json": + schema: + $ref: "../schemas/ProblemDetail.yaml" + 404: + description: Not Found + content: + "application/json": + schema: + $ref: "../schemas/ProblemDetail.yaml" + security: + - httpBasic: [ ] diff --git a/openapi/recipe-api-bundled.yaml b/openapi/recipe-api-bundled.yaml new file mode 100644 index 0000000..504a0b6 --- /dev/null +++ b/openapi/recipe-api-bundled.yaml @@ -0,0 +1,497 @@ +openapi: 3.1.1 +info: + title: Recipe API + description: Programmatic access to a database of recipes. Users can publish and find recipes. + version: 1.0.0 + license: + name: MIT + url: https://opensource.org/licenses/MIT +servers: + - url: https://api.recipes.bitfield.eu/v1 +security: [] +tags: + - name: Recipe + description: Steps and ingredients for preparing a dish + - name: Account + description: Access to protected operations +paths: + /recipe/new: + post: + tags: + - Recipe + summary: Add a recipe + operationId: addRecipe + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Recipe' + examples: + recipeIn: + $ref: '#/components/examples/HummusV1In' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Recipe' + examples: + recipeOut: + $ref: '#/components/examples/HummusV1Out' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetail' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetail' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetail' + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetail' + security: + - httpBasic: [] + /recipe/search: + get: + tags: + - Recipe + summary: Find recipes + operationId: FindRecipes + parameters: + - name: category + in: query + required: false + schema: + type: string + description: Category name filter (equals, ignores case) + - name: recipe + in: query + required: false + schema: + type: string + description: Recipe name filter (contains, ignores case) + - name: limit + in: query + required: true + schema: + type: integer + format: int64 + minimum: 1 + maximum: 100 + description: Maximum number of recipes to return + - name: offset + in: query + required: true + schema: + type: integer + format: int64 + minimum: 0 + description: Index into result set from where recipes will start to be returned + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Recipe' + examples: + dipsOut: + $ref: '#/components/examples/DipsOut' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetail' + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetail' + /recipe/{recipeId}: + get: + tags: + - Recipe + summary: Get a recipe + operationId: GetRecipe + parameters: + - $ref: '#/components/parameters/{recipeId}' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Recipe' + examples: + recipeOut: + $ref: '#/components/examples/HummusV1Out' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetail' + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetail' + put: + tags: + - Recipe + summary: Update a recipe + operationId: UpdateRecipe + parameters: + - $ref: '#/components/parameters/{recipeId}' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Recipe' + examples: + recipeIn: + $ref: '#/components/examples/HummusV2In' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Recipe' + examples: + recipeOut: + $ref: '#/components/examples/HummusV2Out' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetail' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetail' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetail' + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetail' + security: + - httpBasic: [] + delete: + tags: + - Recipe + summary: Remove a recipe + operationId: RemoveRecipe + parameters: + - $ref: '#/components/parameters/{recipeId}' + responses: + '200': + description: OK + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetail' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetail' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetail' + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetail' + security: + - httpBasic: [] + /account/register: + post: + tags: + - Account + summary: Register an account + operationId: RegisterAccount + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + examples: + accountIn: + $ref: '#/components/examples/AccountIn' + required: true + responses: + '200': + description: OK + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetail' +components: + securitySchemes: + httpBasic: + type: http + scheme: basic + schemas: + Id: + type: integer + format: int64 + NonBlankString: + type: string + pattern: ^(.|\s)*\S(.|\s)*$ + DateTime: + type: string + format: date-time + Recipe: + type: object + properties: + id: + $ref: '#/components/schemas/Id' + readOnly: true + description: Unique identifier of the recipe + name: + $ref: '#/components/schemas/NonBlankString' + description: Name + description: + $ref: '#/components/schemas/NonBlankString' + description: Description + changedAt: + $ref: '#/components/schemas/DateTime' + readOnly: true + description: Last point in time when the recipe was changed + categories: + type: array + items: + $ref: '#/components/schemas/NonBlankString' + description: Category names + ingredients: + type: array + minItems: 1 + items: + $ref: '#/components/schemas/NonBlankString' + description: Step descriptions + steps: + type: array + minItems: 1 + items: + $ref: '#/components/schemas/NonBlankString' + description: Ingredient names + required: + - name + - description + - categories + - ingredients + - steps + ProblemDetail: + type: object + properties: + type: + type: string + description: URI reference that identifies the problem type. + format: uri + status: + type: integer + description: HTTP status code generated by the origin server for this occurrence of the problem. + format: int32 + title: + type: string + description: Short, human-readable summary of the problem type. It should not change from occurrence to occurrence of the problem, except for purposes of localization. + detail: + type: string + description: Human-readable explanation specific to this occurrence of the problem. + instance: + type: string + description: URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced. + properties: + type: object + description: Map for additional, non-standard properties + required: + - title + Email: + type: string + format: email + description: Email address + Password: + type: string + format: password + minLength: 8 + description: Password + Account: + type: object + properties: + email: + $ref: '#/components/schemas/Email' + password: + $ref: '#/components/schemas/Password' + required: + - email + - password + examples: + HummusV1In: + summary: Hummus recipe + value: + name: Hummus + description: Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic. + categories: + - Dip + - Spread + ingredients: + - Chickpeas + - Tahini + - Olive oil + - Lemon juice + - Garlic + steps: + - Combine chickpeas, tahini, olive oil, lemon juice, garlic, and salt in a food processor. + - Blend until smooth, slowly adding cold water until you reach a creamy consistency. + - Taste and adjust seasoning if needed. + HummusV1Out: + summary: Hummus recipe + value: + id: 1729 + name: Hummus + description: Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic. + changedAt: '2025-08-08T15:35:10.294411Z' + categories: + - Dip + - Spread + ingredients: + - Chickpeas + - Tahini + - Olive oil + - Lemon juice + - Garlic + steps: + - Combine chickpeas, tahini, olive oil, lemon juice, garlic, and salt in a food processor. + - Blend until smooth, slowly adding cold water until you reach a creamy consistency. + - Taste and adjust seasoning if needed. + DipsOut: + summary: Dip category search + value: + - id: 3 + name: Muhammara + description: Muhammara is a vibrant and flavorful dip or spread from the Middle East, particularly popular in Syrian and Turkish cuisine. It's made primarily from roasted red bell peppers, walnuts, breadcrumbs, and pomegranate molasses. + changedAt: '2025-08-08T16:24:44.673034Z' + categories: + - Dip + ingredients: + - Pomegranate molasses + - Red bell peppers + - Bread crumbs + - Cumin + - Chili flakes + - Garlic + - Walnuts + - Lemon juice + steps: + - Roast whole bell peppers in an oven or air fryer and wait until blackened on the outside. + - Put the peppers in a bowl cover them with to let steam and cool down + - Remove core seeds, and skins + - Combine all the ingredients in a food processor. + - Process until mostly smooth but with a little texture remaining. + - Taste and adjust flavor as needed (lemon for acidity, pomegranate molasses for sweetness) + - id: 2 + name: Hummus + description: Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic. + changedAt: '2025-08-08T16:24:44.579213Z' + categories: + - Dip + ingredients: + - Chickpeas + - Tahini + - Lemon juice + - Garlic + steps: + - Combine chickpeas, tahini, lemon juice, garlic, and salt in a food processor. + - Blend until smooth, slowly adding cold water until you reach a creamy consistency. + - Taste and adjust seasoning if needed. + HummusV2In: + summary: Hummus recipe update + value: + name: Hummus + description: Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic. + categories: + - Dip + - Spread + ingredients: + - Chickpeas + - Tahini + - Lemon juice + - Garlic + steps: + - Combine chickpeas, tahini, lemon juice, garlic, and salt in a food processor. + - Blend until smooth, slowly adding cold water until you reach a creamy consistency. + - Taste and adjust seasoning if needed. + HummusV2Out: + summary: Hummus recipe update + value: + id: 1729 + name: Hummus + description: Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic. + changedAt: '2025-08-09T12:48:32.134821Z' + categories: + - Dip + - Spread + ingredients: + - Chickpeas + - Tahini + - Lemon juice + - Garlic + steps: + - Combine chickpeas, tahini, lemon juice, garlic, and salt in a food processor. + - Blend until smooth, slowly adding cold water until you reach a creamy consistency. + - Taste and adjust seasoning if needed. + AccountIn: + summary: Account registration + value: + email: ada@example.com + password: FBR5h0L1ENA3edctgDIEnxSvFdR0 + parameters: + '{recipeId}': + name: recipeId + in: path + required: true + schema: + $ref: '#/components/schemas/Id' + description: Unique identifier of the recipe diff --git a/openapi/recipe-api.yaml b/openapi/recipe-api.yaml new file mode 100644 index 0000000..b89256c --- /dev/null +++ b/openapi/recipe-api.yaml @@ -0,0 +1,32 @@ +openapi: "3.1.1" +info: + title: "Recipe API" + description: + "Programmatic access to a database of recipes. + Users can publish and find recipes." + version: "1.0.0" + license: + name: MIT + url: https://opensource.org/licenses/MIT +servers: + - url: "https://api.recipes.bitfield.eu/v1" +tags: + - name: Recipe + description: "Steps and ingredients for preparing a dish" + - name: Account + description: "Access to protected operations" +security: [ ] +paths: + /recipe/new: + $ref: "./paths/recipe-new.yaml" + /recipe/search: + $ref: "./paths/recipe-search.yaml" + /recipe/{recipeId}: + $ref: "./paths/recipe-{recipeId}.yaml" + /account/register: + $ref: "./paths/account-register.yaml" +components: + securitySchemes: + httpBasic: + type: http + scheme: basic diff --git a/openapi/redocly.yaml b/openapi/redocly.yaml new file mode 100644 index 0000000..6bc4331 --- /dev/null +++ b/openapi/redocly.yaml @@ -0,0 +1,8 @@ +extends: + - recommended + +apis: + recipe-api@v1: + root: ./recipe-api.yaml + rules: + no-ambiguous-paths: error diff --git a/openapi/schemas/Account.yaml b/openapi/schemas/Account.yaml new file mode 100644 index 0000000..84c8321 --- /dev/null +++ b/openapi/schemas/Account.yaml @@ -0,0 +1,9 @@ +type: "object" +properties: + email: + $ref: "./Email.yaml" + password: + $ref: "./Password.yaml" +required: + - email + - password \ No newline at end of file diff --git a/openapi/schemas/DateTime.yaml b/openapi/schemas/DateTime.yaml new file mode 100644 index 0000000..71840df --- /dev/null +++ b/openapi/schemas/DateTime.yaml @@ -0,0 +1,2 @@ +type: "string" +format: date-time \ No newline at end of file diff --git a/openapi/schemas/Email.yaml b/openapi/schemas/Email.yaml new file mode 100644 index 0000000..051183b --- /dev/null +++ b/openapi/schemas/Email.yaml @@ -0,0 +1,3 @@ +type: "string" +format: email +description: "Email address" \ No newline at end of file diff --git a/openapi/schemas/Id.yaml b/openapi/schemas/Id.yaml new file mode 100644 index 0000000..11a0416 --- /dev/null +++ b/openapi/schemas/Id.yaml @@ -0,0 +1,2 @@ +type: "integer" +format: int64 \ No newline at end of file diff --git a/openapi/schemas/NonBlankString.yaml b/openapi/schemas/NonBlankString.yaml new file mode 100644 index 0000000..2cea67c --- /dev/null +++ b/openapi/schemas/NonBlankString.yaml @@ -0,0 +1,2 @@ +type: "string" +pattern: "^(.|\\s)*\\S(.|\\s)*$" \ No newline at end of file diff --git a/openapi/schemas/Password.yaml b/openapi/schemas/Password.yaml new file mode 100644 index 0000000..7487470 --- /dev/null +++ b/openapi/schemas/Password.yaml @@ -0,0 +1,4 @@ +type: "string" +format: password +minLength: 8 +description: "Password" \ No newline at end of file diff --git a/openapi/schemas/ProblemDetail.yaml b/openapi/schemas/ProblemDetail.yaml new file mode 100644 index 0000000..a9b5e55 --- /dev/null +++ b/openapi/schemas/ProblemDetail.yaml @@ -0,0 +1,24 @@ +type: object +properties: + type: + type: "string" + description: "URI reference that identifies the problem type." + format: "uri" + status: + type: "integer" + description: "HTTP status code generated by the origin server for this occurrence of the problem." + format: "int32" + title: + type: "string" + description: "Short, human-readable summary of the problem type. It should not change from occurrence to occurrence of the problem, except for purposes of localization." + detail: + type: "string" + description: "Human-readable explanation specific to this occurrence of the problem." + instance: + type: "string" + description: "URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced." + properties: + type: "object" + description: "Map for additional, non-standard properties" +required: + - title diff --git a/openapi/schemas/Recipe.yaml b/openapi/schemas/Recipe.yaml new file mode 100644 index 0000000..daf6080 --- /dev/null +++ b/openapi/schemas/Recipe.yaml @@ -0,0 +1,39 @@ +type: "object" +properties: + id: + $ref: "./Id.yaml" + readOnly: true + description: "Unique identifier of the recipe" + name: + $ref: "./NonBlankString.yaml" + description: "Name" + description: + $ref: "./NonBlankString.yaml" + description: "Description" + changedAt: + $ref: "./DateTime.yaml" + readOnly: true + description: "Last point in time when the recipe was changed" + categories: + type: "array" + items: + $ref: "./NonBlankString.yaml" + description: "Category names" + ingredients: + type: "array" + minItems: 1 + items: + $ref: "./NonBlankString.yaml" + description: "Step descriptions" + steps: + type: "array" + minItems: 1 + items: + $ref: "./NonBlankString.yaml" + description: "Ingredient names" +required: + - name + - description + - categories + - ingredients + - steps