From a230ab18efb4ba85768c7dce63e295206f84703f Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Mon, 19 Jan 2026 15:10:13 +0100 Subject: [PATCH 01/34] remove scripts folder --- scripts/check-broken-symlinks.sh | 24 ------------- scripts/check-local-swift-dependencies.sh | 24 ------------- scripts/check-unacceptable-language.sh | 24 ------------- scripts/install-swift-format.sh | 19 ---------- scripts/run-checks.sh | 43 ----------------------- scripts/run-chmod.sh | 10 ------ scripts/run-swift-format.sh | 37 ------------------- scripts/unacceptable-language.txt | 15 -------- 8 files changed, 196 deletions(-) delete mode 100755 scripts/check-broken-symlinks.sh delete mode 100755 scripts/check-local-swift-dependencies.sh delete mode 100755 scripts/check-unacceptable-language.sh delete mode 100755 scripts/install-swift-format.sh delete mode 100755 scripts/run-checks.sh delete mode 100755 scripts/run-chmod.sh delete mode 100755 scripts/run-swift-format.sh delete mode 100755 scripts/unacceptable-language.txt diff --git a/scripts/check-broken-symlinks.sh b/scripts/check-broken-symlinks.sh deleted file mode 100755 index 3d7e919..0000000 --- a/scripts/check-broken-symlinks.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -log() { printf -- "** %s\n" "$*" >&2; } -error() { printf -- "** ERROR: %s\n" "$*" >&2; } -fatal() { error "$@"; exit 1; } - -CURRENT_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -REPO_ROOT="$(git -C "${CURRENT_SCRIPT_DIR}" rev-parse --show-toplevel)" - -log "Checking for broken symlinks..." -NUM_BROKEN_SYMLINKS=0 -while read -r -d '' file; do - if ! test -e "${REPO_ROOT}/${file}"; then - error "Broken symlink: ${file}" - ((NUM_BROKEN_SYMLINKS++)) - fi -done < <(git -C "${REPO_ROOT}" ls-files -z) - -if [ "${NUM_BROKEN_SYMLINKS}" -gt 0 ]; then - fatal "❌ Found ${NUM_BROKEN_SYMLINKS} symlinks." -fi - -log "✅ Found 0 symlinks." diff --git a/scripts/check-local-swift-dependencies.sh b/scripts/check-local-swift-dependencies.sh deleted file mode 100755 index 0cccd11..0000000 --- a/scripts/check-local-swift-dependencies.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -log() { printf -- "** %s\n" "$*" >&2; } -error() { printf -- "** ERROR: %s\n" "$*" >&2; } -fatal() { error "$@"; exit 1; } - -CURRENT_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -REPO_ROOT="$(git -C "${CURRENT_SCRIPT_DIR}" rev-parse --show-toplevel)" - -read -ra PATHS_TO_CHECK <<< "$( \ - git -C "${REPO_ROOT}" ls-files -z \ - "Package.swift" \ - | xargs -0 \ -)" - -for FILE_PATH in "${PATHS_TO_CHECK[@]}"; do -echo $FILE_PATH - if [[ $(grep ".package(path:" "${FILE_PATH}"|wc -l) -ne 0 ]] ; then - fatal "❌ The '${FILE_PATH}' file contains local Swift package reference(s)." - fi -done - -log "✅ Found 0 local Swift package dependency references." diff --git a/scripts/check-unacceptable-language.sh b/scripts/check-unacceptable-language.sh deleted file mode 100755 index 3d93707..0000000 --- a/scripts/check-unacceptable-language.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -log() { printf -- "** %s\n" "$*" >&2; } -error() { printf -- "** ERROR: %s\n" "$*" >&2; } -fatal() { error "$@"; exit 1; } - -CURRENT_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -REPO_ROOT="$(git -C "${CURRENT_SCRIPT_DIR}" rev-parse --show-toplevel)" -UNACCEPTABLE_LANGUAGE_PATTERNS_PATH="${CURRENT_SCRIPT_DIR}/unacceptable-language.txt" - -log "Checking for unacceptable language..." -PATHS_WITH_UNACCEPTABLE_LANGUAGE=$(git -C "${REPO_ROOT}" grep \ - -l -F -w \ - -f "${UNACCEPTABLE_LANGUAGE_PATTERNS_PATH}" \ - -- \ - ":(exclude)${UNACCEPTABLE_LANGUAGE_PATTERNS_PATH}" \ -) || true | /usr/bin/paste -s -d " " - - -if [ -n "${PATHS_WITH_UNACCEPTABLE_LANGUAGE}" ]; then - fatal "❌ Found unacceptable language in files: ${PATHS_WITH_UNACCEPTABLE_LANGUAGE}." -fi - -log "✅ Found no unacceptable language." diff --git a/scripts/install-swift-format.sh b/scripts/install-swift-format.sh deleted file mode 100755 index 3fb3333..0000000 --- a/scripts/install-swift-format.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -log() { printf -- "** %s\n" "$*" >&2; } -error() { printf -- "** ERROR: %s\n" "$*" >&2; } -fatal() { error "$@"; exit 1; } - -# https://github.com/apple/swift-format - -VERSION="509.0.0" - -curl -L -o "${VERSION}.tar.gz" "https://github.com/apple/swift-format/archive/refs/tags/${VERSION}.tar.gz" -tar -xf "${VERSION}.tar.gz" -cd "swift-format-${VERSION}" -swift build -c release -install .build/release/swift-format /usr/local/bin/swift-format -cd .. -rm -f "${VERSION}.tar.gz" -rm -rf "swift-format-${VERSION}" diff --git a/scripts/run-checks.sh b/scripts/run-checks.sh deleted file mode 100755 index b42e0f9..0000000 --- a/scripts/run-checks.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -log() { printf -- "** %s\n" "$*" >&2; } -error() { printf -- "** ERROR: %s\n" "$*" >&2; } -fatal() { error "$@"; exit 1; } - -CURRENT_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -NUM_CHECKS_FAILED=0 - -FIX_FORMAT="" -for arg in "$@"; do - if [ "$arg" == "--fix" ]; then - FIX_FORMAT="--fix" - fi -done - -SCRIPT_PATHS=( - "${CURRENT_SCRIPT_DIR}/check-broken-symlinks.sh" - "${CURRENT_SCRIPT_DIR}/check-unacceptable-language.sh" - "${CURRENT_SCRIPT_DIR}/check-local-swift-dependencies.sh" -) - -for SCRIPT_PATH in "${SCRIPT_PATHS[@]}"; do - log "Running ${SCRIPT_PATH}..." - if ! bash "${SCRIPT_PATH}"; then - ((NUM_CHECKS_FAILED+=1)) - fi -done - -log "Running swift-format..." -bash "${CURRENT_SCRIPT_DIR}"/run-swift-format.sh $FIX_FORMAT > /dev/null -FORMAT_EXIT_CODE=$? -if [ $FORMAT_EXIT_CODE -ne 0 ]; then - ((NUM_CHECKS_FAILED+=1)) -fi - -if [ "${NUM_CHECKS_FAILED}" -gt 0 ]; then - fatal "❌ ${NUM_CHECKS_FAILED} check(s) failed." -fi - -log "✅ All check(s) passed." diff --git a/scripts/run-chmod.sh b/scripts/run-chmod.sh deleted file mode 100755 index 783e89d..0000000 --- a/scripts/run-chmod.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -log() { printf -- "** %s\n" "$*" >&2; } -error() { printf -- "** ERROR: %s\n" "$*" >&2; } -fatal() { error "$@"; exit 1; } - -CURRENT_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -REPO_ROOT="$(git -C "${CURRENT_SCRIPT_DIR}" rev-parse --show-toplevel)" -chmod -R oug+x "${REPO_ROOT}/scripts/" \ No newline at end of file diff --git a/scripts/run-swift-format.sh b/scripts/run-swift-format.sh deleted file mode 100755 index f6fd10d..0000000 --- a/scripts/run-swift-format.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -log() { printf -- "** %s\n" "$*" >&2; } -error() { printf -- "** ERROR: %s\n" "$*" >&2; } -fatal() { error "$@"; exit 1; } - -CURRENT_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -REPO_ROOT="$(git -C "${CURRENT_SCRIPT_DIR}" rev-parse --show-toplevel)" - -FORMAT_COMMAND=(lint --strict) -for arg in "$@"; do - if [ "$arg" == "--fix" ]; then - FORMAT_COMMAND=(format --in-place) - fi -done - -SWIFTFORMAT_BIN=${SWIFTFORMAT_BIN:-$(command -v swift-format)} || fatal "❌ SWIFTFORMAT_BIN unset and no swift-format on PATH" - -git -C "${REPO_ROOT}" ls-files -z '*.swift' \ - | grep -z -v \ - -e 'Sources/CoreOpenAPIRuntimeKit/Types.swift' \ - -e 'Package.swift' \ - | xargs -0 "${SWIFTFORMAT_BIN}" "${FORMAT_COMMAND[@]}" --parallel \ - && SWIFT_FORMAT_RC=$? || SWIFT_FORMAT_RC=$? - -if [ "${SWIFT_FORMAT_RC}" -ne 0 ]; then - fatal "❌ Running swift-format produced errors. - - To fix, run the following command: - - % ./scripts/run-swift-format.sh --fix - " - exit "${SWIFT_FORMAT_RC}" -fi - -log "✅ Ran swift-format with no errors." diff --git a/scripts/unacceptable-language.txt b/scripts/unacceptable-language.txt deleted file mode 100755 index 6ac4a98..0000000 --- a/scripts/unacceptable-language.txt +++ /dev/null @@ -1,15 +0,0 @@ -blacklist -whitelist -slave -master -sane -sanity -insane -insanity -kill -killed -killing -hang -hung -hanged -hanging From 85c47b35c9c02d69e5df50e57e35da009b22103a Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Tue, 20 Jan 2026 19:55:05 +0100 Subject: [PATCH 02/34] dep update, compilation fixes --- AGENTS.md | 80 +++++++++++++ LICENSE | 2 +- Makefile | 52 +++++---- Package.resolved | 15 +-- Package.swift | 110 +++++++++--------- README.md | 98 +++++++++++----- .../Extensions/JSONSchema+Enumeration.swift | 0 .../Extensions/JSONSchema+Text.swift | 0 .../OrderedDictionary+Composition.swift | 0 .../String+LowercasedFirstLetter.swift | 0 .../Interfaces/Component.swift | 0 .../Interfaces/Document.swift | 21 ++-- .../Interfaces/Header/Header.swift | 0 .../Interfaces/Identifiable.swift | 5 +- .../Interfaces/Operation/Operation.swift | 0 .../Operation/OperationResponse.swift | 0 .../Parameter/HeaderParameter.swift | 27 +++++ .../Interfaces/Parameter/Parameter.swift | 4 +- .../Interfaces/Parameter/PathParameter.swift | 7 +- .../Interfaces/Parameter/QueryParameter.swift | 29 +++++ .../Interfaces/PathItem/Path.swift | 0 .../Interfaces/PathItem/PathItem.swift | 0 .../Interfaces/RequestBody/BinaryBody.swift | 9 +- .../Interfaces/RequestBody/FormBody.swift | 2 +- .../Interfaces/RequestBody/JSONBody.swift | 2 +- .../Interfaces/RequestBody/RequestBody.swift | 0 .../Interfaces/Response/BinaryResponse.swift | 13 ++- .../Interfaces/Response/JSONResponse.swift | 0 .../Interfaces/Response/Response.swift | 4 +- .../Interfaces/Schema/ArraySchema.swift | 0 .../Interfaces/Schema/BooleanSchema.swift | 0 .../Interfaces/Schema/DateTimeSchema.swift | 0 .../Interfaces/Schema/DoubleSchema.swift | 0 .../Interfaces/Schema/EmailSchema.swift | 0 .../Interfaces/Schema/EnumSchema.swift | 0 .../Interfaces/Schema/FloatSchema.swift | 0 .../Interfaces/Schema/IDSchema.swift | 0 .../Interfaces/Schema/Int32Schema.swift | 0 .../Interfaces/Schema/Int64Schema.swift | 0 .../Interfaces/Schema/IntSchema.swift | 0 .../Interfaces/Schema/NanoIDSchema.swift | 0 .../Interfaces/Schema/NumberSchema.swift | 0 .../Interfaces/Schema/ObjectSchema.swift | 0 .../Schema/ObjectSchemaProperty.swift | 0 .../Interfaces/Schema/PasswordSchema.swift | 0 .../Interfaces/Schema/Schema.swift | 0 .../Interfaces/Schema/TextSchema.swift | 0 .../Interfaces/Schema/UUIDSchema.swift | 0 .../SecurityScheme/SecurityScheme.swift | 0 .../Interfaces/Tag/Tag.swift | 0 .../Parameter/HeaderParameter.swift | 17 --- .../Interfaces/Parameter/QueryParameter.swift | 19 --- .../FeatherOpenAPIKitMacros.swift | 13 --- .../FeatherOpenAPIKitMacrosKit.swift | 17 --- .../Macros/ComponentCollectionMacro.swift | 100 ---------------- .../feather-openapi-generator.swift | 7 +- .../FeatherOpenAPIKitMacrosTests.swift | 96 --------------- .../FeatherOpenAPIKitMacrosTests.swift | 38 ------ .../Model/ExampleModel+Operations.swift | 47 -------- .../Example/Components/Example/Example.swift | 0 .../Example/Model/ExampleModel+Headers.swift | 2 +- .../Model/ExampleModel+Operations.swift | 55 +++++++++ .../Model/ExampleModel+Parameters.swift | 16 +-- .../Model/ExampleModel+PathItems.swift | 2 +- .../Model/ExampleModel+RequestBodies.swift | 2 +- .../Model/ExampleModel+Responses.swift | 2 +- .../Example/Model/ExampleModel+Schemas.swift | 72 +++++++----- .../Model/ExampleModel+SecuritySchemes.swift | 2 +- .../Example/Model/ExampleModel+Tags.swift | 2 +- .../Example/Model/ExampleModel.swift | 2 +- .../ExampleDuplicatedItem.swift | 0 .../ExampleDuplicatedItemModel+Schemas.swift | 2 +- .../Model/ExampleDuplicatedItemModel.swift | 2 +- .../ExampleMissingParentItem.swift | 0 ...xampleMissingParentItemModel+Schemas.swift | 2 +- .../Model/ExampleMissingParentItemModel.swift | 2 +- .../Example/ExampleDocument.swift | 2 +- .../ExampleDuplicatedItemDocument.swift | 2 +- .../ExampleMissingParentItemDocument.swift | 2 +- .../FeatherOpenAPIKitTests.swift | 2 +- .../PathComponentTests.swift | 2 +- docker/tests/Dockerfile | 11 ++ 82 files changed, 481 insertions(+), 539 deletions(-) create mode 100644 AGENTS.md rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Extensions/JSONSchema+Enumeration.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Extensions/JSONSchema+Text.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Extensions/OrderedDictionary+Composition.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Extensions/String+LowercasedFirstLetter.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Component.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Document.swift (86%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Header/Header.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Identifiable.swift (82%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Operation/Operation.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Operation/OperationResponse.swift (100%) create mode 100644 Sources/FeatherOpenAPI/Interfaces/Parameter/HeaderParameter.swift rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Parameter/Parameter.swift (91%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Parameter/PathParameter.swift (56%) create mode 100644 Sources/FeatherOpenAPI/Interfaces/Parameter/QueryParameter.swift rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/PathItem/Path.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/PathItem/PathItem.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/RequestBody/BinaryBody.swift (69%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/RequestBody/FormBody.swift (91%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/RequestBody/JSONBody.swift (91%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/RequestBody/RequestBody.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Response/BinaryResponse.swift (54%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Response/JSONResponse.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Response/Response.swift (92%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Schema/ArraySchema.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Schema/BooleanSchema.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Schema/DateTimeSchema.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Schema/DoubleSchema.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Schema/EmailSchema.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Schema/EnumSchema.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Schema/FloatSchema.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Schema/IDSchema.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Schema/Int32Schema.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Schema/Int64Schema.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Schema/IntSchema.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Schema/NanoIDSchema.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Schema/NumberSchema.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Schema/ObjectSchema.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Schema/ObjectSchemaProperty.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Schema/PasswordSchema.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Schema/Schema.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Schema/TextSchema.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Schema/UUIDSchema.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/SecurityScheme/SecurityScheme.swift (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Interfaces/Tag/Tag.swift (100%) delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Parameter/HeaderParameter.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Parameter/QueryParameter.swift delete mode 100644 Sources/FeatherOpenAPIKitMacros/FeatherOpenAPIKitMacros.swift delete mode 100644 Sources/FeatherOpenAPIKitMacrosKit/FeatherOpenAPIKitMacrosKit.swift delete mode 100644 Sources/FeatherOpenAPIKitMacrosKit/Macros/ComponentCollectionMacro.swift delete mode 100644 Tests/FeatherOpenAPIKitMacrosKitTests/FeatherOpenAPIKitMacrosTests.swift delete mode 100644 Tests/FeatherOpenAPIKitMacrosTests/FeatherOpenAPIKitMacrosTests.swift delete mode 100644 Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Operations.swift rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/Components/Example/Example.swift (100%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/Components/Example/Model/ExampleModel+Headers.swift (94%) create mode 100644 Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/Components/Example/Model/ExampleModel+Parameters.swift (59%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/Components/Example/Model/ExampleModel+PathItems.swift (96%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/Components/Example/Model/ExampleModel+RequestBodies.swift (93%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/Components/Example/Model/ExampleModel+Responses.swift (96%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/Components/Example/Model/ExampleModel+Schemas.swift (54%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift (97%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/Components/Example/Model/ExampleModel+Tags.swift (91%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/Components/Example/Model/ExampleModel.swift (83%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/Components/ExampleDuplicatedItem/ExampleDuplicatedItem.swift (100%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift (96%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift (84%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/Components/ExampleMissingParentItem/ExampleMissingParentItem.swift (100%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift (95%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift (84%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/ExampleDocument.swift (97%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/ExampleDuplicatedItemDocument.swift (97%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/ExampleMissingParentItemDocument.swift (97%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/FeatherOpenAPIKitTests.swift (98%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/PathComponentTests.swift (98%) create mode 100644 docker/tests/Dockerfile diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..157e1a4 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,80 @@ +# Repository Guidelines + +This repository contains an project written with Swift 6. Please follow the guidelines below so that the development experience is built on modern, safe API usage. + + +## Role + +You are a **Senior Swift Engineer**, specializing in server-side Swift development, and related frameworks (Vapor, Hummingbird). + + +## Core instructions + +- Swift 6.2 or later, using modern Swift concurrency. +- Do not introduce third-party frameworks without asking first. +- Avoid Foundation unless requested. +- Build system: Swift Package Manager. +- Testing framework: Swift Testing (`swift test`). + + +## Swift instructions + +- Assume strict Swift concurrency rules are being applied. +- Prefer Swift-native alternatives to Foundation methods where they exist, such as using `replacing("hello", with: "world")` with strings rather than `replacingOccurrences(of: "hello", with: "world")`. +- Prefer modern Foundation API, if Foundation can not be avoided, for example `URL.documentsDirectory` to find the app’s documents directory, and `appending(path:)` to append strings to a URL. +- Never use C-style number formatting such as `Text(String(format: "%.2f", abs(myNumber)))`; always use `Text(abs(change), format: .number.precision(.fractionLength(2)))` instead. +- Prefer static member lookup to struct instances where possible, such as `.circle` rather than `Circle()`, and `.borderedProminent` rather than `BorderedProminentButtonStyle()`. +- Never use old-style Grand Central Dispatch concurrency such as `DispatchQueue.main.async()`. If behavior like this is needed, always use modern Swift concurrency. +- Avoid force unwraps and force `try` unless it is unrecoverable. +- Never use `Task.sleep(nanoseconds:)`; always use `Task.sleep(for:)` instead. + + +## Project structure + +- Use a consistent project structure. +- Follow strict naming conventions for types, properties, methods, and data models. +- Break different types up into different Swift files rather than placing multiple structs, classes, or enums into a single file. +- Write unit tests for core application logic. +- Add code comments and documentation comments as needed. +- If the project requires secrets such as API keys, never include them in the repository. + + +## Build, Test, and Development Commands + +- Preferred workflow: after any code change run `swift build` to rebuild the project, and run `swift test` when you actually need tests. + + +## PR instructions + +- If installed, make sure the `make format` & `make check` commands returns no warnings or errors before committing. + + +## Coding Style & Naming Conventions + +- Enforce formatting with the `make format` command. +- Swift 6.2, prefer strict typing and small files (<500 LOC as a guardrail) +- Naming: types UpperCamelCase; methods/properties lowerCamelCase; tests mirror subject names; avoid abbreviations except common GitHub/API terms. + + +## Testing Guidelines + +- Framework: Swift Testing via `swift test`. Name suites `Tests` and functions `behavior()`. +- Cover new logic. Use deterministic fixtures/mocks for data. +- Run `swift test` before pushing; prefer adding tests alongside bug fixes. + + +## Commit & Pull Request Guidelines + +- Commit messages follow the existing short, imperative style; optional scoped prefixes. Keep them concise; present tense; no trailing period. +- PRs: include a brief summary, linked issue ticket if any. + + +## Security & Configuration Tips + +- Keep GitHub App secrets/private key out of the repo; +- Do not log tokens or traffic stats responses; prefer redacted diagnostics. + + +## Agent-Specific Notes + +- Reminder: ignore files you do not recognize (just list them); multiple agents often work here. diff --git a/LICENSE b/LICENSE index 9a07ee3..d48549a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ MIT License Copyright (c) 2018-2022 Tibor Bödecs -Copyright (c) 2022-2024 Binary Birds Ltd. +Copyright (c) 2022-2026 Binary Birds Kft. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/Makefile b/Makefile index 26b3820..a3e1b32 100644 --- a/Makefile +++ b/Makefile @@ -1,29 +1,41 @@ -build: - swift build +SHELL=/bin/bash -release: - swift build -c release - -test: - swift test --parallel +baseUrl = https://raw.githubusercontent.com/BinaryBirds/github-workflows/refs/heads/main/scripts -test-with-coverage: - swift test --parallel --enable-code-coverage +check: symlinks language deps lint headers -clean: - rm -rf .build +symlinks: + curl -s $(baseUrl)/check-broken-symlinks.sh | bash -check: - ./scripts/run-checks.sh +language: + curl -s $(baseUrl)/check-unacceptable-language.sh | bash + +deps: + curl -s $(baseUrl)/check-local-swift-dependencies.sh | bash + +lint: + curl -s $(baseUrl)/run-swift-format.sh | bash format: - ./scripts/run-swift-format.sh --fix + curl -s $(baseUrl)/run-swift-format.sh | bash -s -- --fix + +docc-local: + curl -s $(baseUrl)/generate-docc.sh | bash -s -- --local -release: build - swift build -c release +run-docc: + curl -s $(baseUrl)/run-docc-docker.sh | bash -install: release - install .build/release/feather-openapi-generator /usr/local/bin/feather-openapi-generator +docc-warnings: + curl -s $(baseUrl)/check-docc-warnings.sh | bash + +headers: + curl -s $(baseUrl)/check-swift-headers.sh | bash + +fix-headers: + curl -s $(baseUrl)/check-swift-headers.sh | bash -s -- --fix + +test: + swift test --parallel -uninstall: - rm /usr/local/bin/feather-openapi-generator +docker-test: + docker build -t feather-database-tests . -f ./docker/tests/Dockerfile && docker run --rm feather-database-tests diff --git a/Package.resolved b/Package.resolved index 51b4c4d..83587b1 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,12 +1,13 @@ { + "originHash" : "8f98dd02ffc2297204019bd52be53aa9500ecde4ef10fb829fd78a13b4b83f8f", "pins" : [ { "identity" : "openapikit", "kind" : "remoteSourceControl", "location" : "https://github.com/mattpolzin/OpenAPIKit", "state" : { - "revision" : "33a9984b4af03f00e68b8ee85f1273cb826af04f", - "version" : "3.1.3" + "revision" : "d267e29373b3d2ff395d3b46d8bf2085a9263f67", + "version" : "5.0.0-rc.2" } }, { @@ -14,8 +15,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-syntax.git", "state" : { - "revision" : "64889f0c732f210a935a0ad7cda38f77f876262d", - "version" : "509.1.1" + "revision" : "4799286537280063c85a32f09884cfbca301b1a1", + "version" : "602.0.0" } }, { @@ -23,10 +24,10 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/jpsim/Yams", "state" : { - "revision" : "0d9ee7ea8c4ebd4a489ad7a73d5c6cad55d6fed3", - "version" : "5.0.6" + "revision" : "51b5127c7fb6ffac106ad6d199aaa33c5024895f", + "version" : "6.2.0" } } ], - "version" : 2 + "version" : 3 } diff --git a/Package.swift b/Package.swift index f25003c..bfd564e 100644 --- a/Package.swift +++ b/Package.swift @@ -1,83 +1,89 @@ -// swift-tools-version:5.9 +// swift-tools-version:6.1 import PackageDescription + import CompilerPluginSupport +// NOTE: https://github.com/swift-server/swift-http-server/blob/main/Package.swift +var defaultSwiftSettings: [SwiftSetting] = +[ + // https://github.com/swiftlang/swift-evolution/blob/main/proposals/0441-formalize-language-mode-terminology.md + .swiftLanguageMode(.v6), + // https://github.com/swiftlang/swift-evolution/blob/main/proposals/0444-member-import-visibility.md + .enableUpcomingFeature("MemberImportVisibility"), + // https://forums.swift.org/t/experimental-support-for-lifetime-dependencies-in-swift-6-2-and-beyond/78638 + .enableExperimentalFeature("Lifetimes"), + // https://github.com/swiftlang/swift/pull/65218 + .enableExperimentalFeature("AvailabilityMacro=featherDatabase 1.0:macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, visionOS 2.0"), +] + +#if compiler(>=6.2) +defaultSwiftSettings.append( + // https://github.com/swiftlang/swift-evolution/blob/main/proposals/0461-async-function-isolation.md + .enableUpcomingFeature("NonisolatedNonsendingByDefault") +) +#endif + let package = Package( - name: "feather-openapi-kit", + name: "feather-openapi", platforms: [ - .macOS(.v13), - .iOS(.v16), - .tvOS(.v16), - .watchOS(.v9), - .visionOS(.v1), + .macOS(.v15), + .iOS(.v18), + .tvOS(.v18), + .watchOS(.v11), + .visionOS(.v2), ], products: [ - .library(name: "FeatherOpenAPIKit", targets: ["FeatherOpenAPIKit"]), - .library(name: "FeatherOpenAPIKitMacros", targets: ["FeatherOpenAPIKitMacros"]), - .plugin(name: "FeatherOpenAPIGenerator", targets: ["FeatherOpenAPIGenerator"]), - .executable(name: "feather-openapi-generator", targets: ["feather-openapi-generator"]), + .executable( + name: "feather-openapi-generator", + targets: ["feather-openapi-generator"] + ), + .library( + name: "FeatherOpenAPI", + targets: ["FeatherOpenAPI"] + ), + .plugin( + name: "FeatherOpenAPIGenerator", + targets: ["FeatherOpenAPIGenerator"] + ), ], dependencies: [ - .package(url: "https://github.com/mattpolzin/OpenAPIKit", from: "3.1.0"), - .package(url: "https://github.com/jpsim/Yams", from: "5.0.0"), - .package(url: "https://github.com/apple/swift-syntax", from: "509.0.0"), + .package(url: "https://github.com/mattpolzin/OpenAPIKit", exact: "5.0.0-rc.2"), + .package(url: "https://github.com/apple/swift-syntax", exact: "602.0.0"), + .package(url: "https://github.com/jpsim/Yams", from: "6.2.0"), ], targets: [ - .macro( - name: "FeatherOpenAPIKitMacrosKit", - dependencies: [ - .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), - .product(name: "SwiftCompilerPlugin", package: "swift-syntax") - ] - ), - .target( - name: "FeatherOpenAPIKitMacros", + .plugin( + name: "FeatherOpenAPIGenerator", + capability: .buildTool(), dependencies: [ - .target(name: "FeatherOpenAPIKitMacrosKit"), - ] + .target(name: "feather-openapi-generator") + ], ), + // MARK: - .target( - name: "FeatherOpenAPIKit", + name: "FeatherOpenAPI", dependencies: [ .product(name: "OpenAPIKit", package: "OpenAPIKit"), ], + swiftSettings: defaultSwiftSettings, plugins: [ .plugin(name: "FeatherOpenAPIGenerator") - ] + ], ), .executableTarget( name: "feather-openapi-generator", dependencies: [ .product(name: "SwiftParser", package: "swift-syntax") - ] - ), - .plugin( - name: "FeatherOpenAPIGenerator", - capability: .buildTool(), - dependencies: [ - .target(name: "feather-openapi-generator") - ] + ], + swiftSettings: defaultSwiftSettings, ), .testTarget( - name: "FeatherOpenAPIKitTests", + name: "FeatherOpenAPITests", dependencies: [ .product(name: "Yams", package: "Yams"), - .target(name: "FeatherOpenAPIKit"), - ] - ), - .testTarget( - name: "FeatherOpenAPIKitMacrosKitTests", - dependencies: [ - .product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"), - .target(name: "FeatherOpenAPIKitMacrosKit"), - ] - ), - .testTarget( - name: "FeatherOpenAPIKitMacrosTests", - dependencies: [ - .target(name: "FeatherOpenAPIKitMacros"), - .target(name: "FeatherOpenAPIKit"), - ] + .target(name: "FeatherOpenAPI"), + ], + swiftSettings: defaultSwiftSettings, ), ] ) diff --git a/README.md b/README.md index 39e07ab..8312927 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,84 @@ -# Feather OpenAPI Kit +# Feather OpenAPI -The `FeatherOpenAPIKit` library provides generic solutions for both OpenAPI objects. +The FeatherOpenAPI library makes it easy to define OpenAPI specifications using Swift in a type-safe way. -## Current components: +![Release: 1.0.0-beta.1](https://img.shields.io/badge/Release-1%2E0%2E0--beta%2E1-F05138) -- A generic solution for listing objects including filter, sort and page info. +## Features -## Getting started +- 🤝 Type-safe interface for building OpenAPI documents +- 🔀 Automatic identifier generation and resolution +- 📚 DocC-based API Documentation +- ✅ Unit tests and code coverage -⚠️ This repository is a work in progress, things can break until it reaches v1.0.0. +## Requirements -Use at your own risk. +![Swift 6.1+](https://img.shields.io/badge/Swift-6%2E1%2B-F05138) +![Platforms: Linux, macOS, iOS, tvOS, watchOS, visionOS](https://img.shields.io/badge/Platforms-Linux_%7C_macOS_%7C_iOS_%7C_tvOS_%7C_watchOS_%7C_visionOS-F05138) + +- Swift 6.1+ -### Adding the dependency +- Platforms: + - Linux + - macOS 15+ + - iOS 18+ + - tvOS 18+ + - watchOS 11+ + - visionOS 2+ -To add a dependency on the package, declare it in your `Package.swift`: +## Installation + +Use Swift Package Manager; add the dependency to your `Package.swift` file: ```swift -.package(url: "https://github.com/feather-framework/feather-openapi-kit", .upToNextMinor(from: "0.5.0")), +.package(url: "https://github.com/feather-framework/feather-openapi", exact: "1.0.0-beta.1"), ``` -and to your application target, add `FeatherKit` to your dependencies: +Then add `FeatherOpenAPI` to your target dependencies: ```swift -.product(name: "FeatherOpenAPIKit", package: "feather-openapi-kit") +.product(name: "FeatherOpenAPI", package: "feather-openapi"), ``` -Example `Package.swift` file with `FeatherOpenAPIKit` as a dependency: +## Usage + +![DocC API documentation](https://img.shields.io/badge/DocC-API_documentation-F05138) + +API documentation is available at the following link. Refer to the mock objects in the Tests directory if you want to build a custom database driver implementation. + +> [!WARNING] +> This repository is a work in progress, things can break until it reaches v1.0.0. + + +## Development + +- Build: `swift build` +- Test: + - local: `swift test` + - using Docker: `make docker-test` +- Format: `make format` +- Check: `make check` + +## Contributing + +[Pull requests](https://github.com/feather-framework/feather-openapi/pulls) are welcome. Please keep changes focused and include tests for new logic. 🙏 + + + + + + + + + + + + + + + + + + -```swift -// swift-tools-version:5.9 -import PackageDescription - -let package = Package( - name: "my-application", - dependencies: [ - .package(url: "https://github.com/feather-framework/feather-openapi-kit", .upToNextMinor(from: "0.5.0")), - ], - targets: [ - .target(name: "MyApplication", dependencies: [ - .product(name: "FeatherOpenAPIKit", package: "feather-openapi-kit") - ]), - .testTarget(name: "MyApplicationTests", dependencies: [ - .target(name: "MyApplication"), - ]), - ] -) -``` diff --git a/Sources/FeatherOpenAPIKit/Extensions/JSONSchema+Enumeration.swift b/Sources/FeatherOpenAPI/Extensions/JSONSchema+Enumeration.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Extensions/JSONSchema+Enumeration.swift rename to Sources/FeatherOpenAPI/Extensions/JSONSchema+Enumeration.swift diff --git a/Sources/FeatherOpenAPIKit/Extensions/JSONSchema+Text.swift b/Sources/FeatherOpenAPI/Extensions/JSONSchema+Text.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Extensions/JSONSchema+Text.swift rename to Sources/FeatherOpenAPI/Extensions/JSONSchema+Text.swift diff --git a/Sources/FeatherOpenAPIKit/Extensions/OrderedDictionary+Composition.swift b/Sources/FeatherOpenAPI/Extensions/OrderedDictionary+Composition.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Extensions/OrderedDictionary+Composition.swift rename to Sources/FeatherOpenAPI/Extensions/OrderedDictionary+Composition.swift diff --git a/Sources/FeatherOpenAPIKit/Extensions/String+LowercasedFirstLetter.swift b/Sources/FeatherOpenAPI/Extensions/String+LowercasedFirstLetter.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Extensions/String+LowercasedFirstLetter.swift rename to Sources/FeatherOpenAPI/Extensions/String+LowercasedFirstLetter.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Component.swift b/Sources/FeatherOpenAPI/Interfaces/Component.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Component.swift rename to Sources/FeatherOpenAPI/Interfaces/Component.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Document.swift b/Sources/FeatherOpenAPI/Interfaces/Document.swift similarity index 86% rename from Sources/FeatherOpenAPIKit/Interfaces/Document.swift rename to Sources/FeatherOpenAPI/Interfaces/Document.swift index e997363..0c1d073 100644 --- a/Sources/FeatherOpenAPIKit/Interfaces/Document.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Document.swift @@ -81,7 +81,7 @@ public extension Document { } } - func parameters() throws -> OpenAPI.ComponentDictionary { + func parameters() throws -> OpenAPI.ComponentReferenceDictionary { return try Self.filterIdentifiables( lists: components.map { @@ -89,11 +89,11 @@ public extension Document { } ) .reduce(into: [:]) { into, item in - into[item.componentKey] = item.openAPIParameter() + into[item.componentKey] = .init(item.openAPIParameter()) } } - func headers() throws -> OpenAPI.ComponentDictionary { + func headers() throws -> OpenAPI.ComponentReferenceDictionary { return try Self.filterIdentifiables( lists: components.map { @@ -101,11 +101,11 @@ public extension Document { } ) .reduce(into: [:]) { into, item in - into[item.componentKey] = item.openAPIHeader() + into[item.componentKey] = .init(item.openAPIHeader()) } } - func requestBodies() throws -> OpenAPI.ComponentDictionary + func requestBodies() throws -> OpenAPI.ComponentReferenceDictionary { return try Self.filterIdentifiables( @@ -114,12 +114,12 @@ public extension Document { } ) .reduce(into: [:]) { into, item in - into[item.componentKey] = item.openAPIRequestBody() + into[item.componentKey] = .init(item.openAPIRequestBody()) } } func securitySchemes() throws - -> OpenAPI.ComponentDictionary + -> OpenAPI.ComponentReferenceDictionary { return try Self.filterIdentifiables( @@ -128,11 +128,12 @@ public extension Document { } ) .reduce(into: [:]) { into, item in - into[item.componentKey] = item.openAPISecurityScheme() + into[item.componentKey] = .init(item.openAPISecurityScheme()) } } - func responses() throws -> OpenAPI.ComponentDictionary { + func responses() throws -> OpenAPI.ComponentReferenceDictionary { + return try Self.filterIdentifiables( lists: components.map { @@ -140,7 +141,7 @@ public extension Document { } ) .reduce(into: [:]) { into, item in - into[item.componentKey] = item.openAPIResponse() + into[item.componentKey] = .init(item.openAPIResponse()) } } diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Header/Header.swift b/Sources/FeatherOpenAPI/Interfaces/Header/Header.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Header/Header.swift rename to Sources/FeatherOpenAPI/Interfaces/Header/Header.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Identifiable.swift b/Sources/FeatherOpenAPI/Interfaces/Identifiable.swift similarity index 82% rename from Sources/FeatherOpenAPIKit/Interfaces/Identifiable.swift rename to Sources/FeatherOpenAPI/Interfaces/Identifiable.swift index 4cefb77..0605914 100644 --- a/Sources/FeatherOpenAPIKit/Interfaces/Identifiable.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Identifiable.swift @@ -18,8 +18,9 @@ public extension Identifiable { var components = String(reflecting: self).split(separator: ".") components.remove(at: 0) // remove namespace components.remove(at: 2) // remove enum name - return components.joined(separator: "") - .replacingOccurrences(of: "GenericComponent", with: "Generic") + return components + .joined(separator: "") + .replacing("GenericComponent", with: "Generic") } static var override: Bool { false } diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Operation/Operation.swift b/Sources/FeatherOpenAPI/Interfaces/Operation/Operation.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Operation/Operation.swift rename to Sources/FeatherOpenAPI/Interfaces/Operation/Operation.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Operation/OperationResponse.swift b/Sources/FeatherOpenAPI/Interfaces/Operation/OperationResponse.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Operation/OperationResponse.swift rename to Sources/FeatherOpenAPI/Interfaces/Operation/OperationResponse.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Parameter/HeaderParameter.swift b/Sources/FeatherOpenAPI/Interfaces/Parameter/HeaderParameter.swift new file mode 100644 index 0000000..8c8fe37 --- /dev/null +++ b/Sources/FeatherOpenAPI/Interfaces/Parameter/HeaderParameter.swift @@ -0,0 +1,27 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 15/02/2024. +// + +import OpenAPIKit + +public protocol HeaderParameter: Parameter {} + +public extension HeaderParameter { + + static var context: OpenAPI.Parameter.Context { + .header( + required: Self.required, + schemaOrContent: .schema( + .schema( + Self.schema.openAPISchema(), + style: .default( + for: .header + ) + ) + ) + ) + } +} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Parameter/Parameter.swift b/Sources/FeatherOpenAPI/Interfaces/Parameter/Parameter.swift similarity index 91% rename from Sources/FeatherOpenAPIKit/Interfaces/Parameter/Parameter.swift rename to Sources/FeatherOpenAPI/Interfaces/Parameter/Parameter.swift index d9ca974..5d79b3f 100644 --- a/Sources/FeatherOpenAPIKit/Interfaces/Parameter/Parameter.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Parameter/Parameter.swift @@ -38,8 +38,8 @@ public extension Parameter { static func openAPIParameter() -> OpenAPI.Parameter { .init( name: name, - context: context, - schema: schema.reference(), + context: context, // TODO: fix me +// schema: schema.reference(), description: description ) } diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Parameter/PathParameter.swift b/Sources/FeatherOpenAPI/Interfaces/Parameter/PathParameter.swift similarity index 56% rename from Sources/FeatherOpenAPIKit/Interfaces/Parameter/PathParameter.swift rename to Sources/FeatherOpenAPI/Interfaces/Parameter/PathParameter.swift index de941aa..b429148 100644 --- a/Sources/FeatherOpenAPIKit/Interfaces/Parameter/PathParameter.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Parameter/PathParameter.swift @@ -10,5 +10,10 @@ import OpenAPIKit public protocol PathParameter: Parameter {} public extension PathParameter { - static var context: OpenAPI.Parameter.Context { .path } + + static var context: OpenAPI.Parameter.Context { + .path( + schema: Self.schema.openAPISchema() + ) + } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Parameter/QueryParameter.swift b/Sources/FeatherOpenAPI/Interfaces/Parameter/QueryParameter.swift new file mode 100644 index 0000000..3ca31c5 --- /dev/null +++ b/Sources/FeatherOpenAPI/Interfaces/Parameter/QueryParameter.swift @@ -0,0 +1,29 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 25/01/2024. +// + +import OpenAPIKit + +public protocol QueryParameter: Parameter {} + +public extension QueryParameter { + + static var required: Bool { false } + + static var context: OpenAPI.Parameter.Context { + .query( + required: Self.required, + schemaOrContent: .schema( + .schema( + Self.schema.openAPISchema(), + style: .default( + for: .query + ) + ) + ) + ) + } +} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/PathItem/Path.swift b/Sources/FeatherOpenAPI/Interfaces/PathItem/Path.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/PathItem/Path.swift rename to Sources/FeatherOpenAPI/Interfaces/PathItem/Path.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/PathItem/PathItem.swift b/Sources/FeatherOpenAPI/Interfaces/PathItem/PathItem.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/PathItem/PathItem.swift rename to Sources/FeatherOpenAPI/Interfaces/PathItem/PathItem.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/BinaryBody.swift b/Sources/FeatherOpenAPI/Interfaces/RequestBody/BinaryBody.swift similarity index 69% rename from Sources/FeatherOpenAPIKit/Interfaces/RequestBody/BinaryBody.swift rename to Sources/FeatherOpenAPI/Interfaces/RequestBody/BinaryBody.swift index 7d4cb2f..ea6363a 100644 --- a/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/BinaryBody.swift +++ b/Sources/FeatherOpenAPI/Interfaces/RequestBody/BinaryBody.swift @@ -15,10 +15,13 @@ extension BinaryBody { description: description, content: [ contentType: .init( - schema: .string( - contentMediaType: .other("application/octet-stream") + .init( + schema: .string( + contentMediaType: .other("application/octet-stream") + ), + examples: nil ) - ) + ), ], required: required ) diff --git a/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/FormBody.swift b/Sources/FeatherOpenAPI/Interfaces/RequestBody/FormBody.swift similarity index 91% rename from Sources/FeatherOpenAPIKit/Interfaces/RequestBody/FormBody.swift rename to Sources/FeatherOpenAPI/Interfaces/RequestBody/FormBody.swift index 0829cf9..49d87ae 100644 --- a/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/FormBody.swift +++ b/Sources/FeatherOpenAPI/Interfaces/RequestBody/FormBody.swift @@ -20,7 +20,7 @@ public extension FormBody { .init( description: description, content: [ - .form: schema.reference() + .form: .init(schema.reference()) ], required: required ) diff --git a/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/JSONBody.swift b/Sources/FeatherOpenAPI/Interfaces/RequestBody/JSONBody.swift similarity index 91% rename from Sources/FeatherOpenAPIKit/Interfaces/RequestBody/JSONBody.swift rename to Sources/FeatherOpenAPI/Interfaces/RequestBody/JSONBody.swift index 04fe4cc..4e64daa 100644 --- a/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/JSONBody.swift +++ b/Sources/FeatherOpenAPI/Interfaces/RequestBody/JSONBody.swift @@ -21,7 +21,7 @@ public extension JSONBody { .init( description: description, content: [ - .json: schema.reference() + .json: .init(schema.reference()) ], required: required ) diff --git a/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/RequestBody.swift b/Sources/FeatherOpenAPI/Interfaces/RequestBody/RequestBody.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/RequestBody/RequestBody.swift rename to Sources/FeatherOpenAPI/Interfaces/RequestBody/RequestBody.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Response/BinaryResponse.swift b/Sources/FeatherOpenAPI/Interfaces/Response/BinaryResponse.swift similarity index 54% rename from Sources/FeatherOpenAPIKit/Interfaces/Response/BinaryResponse.swift rename to Sources/FeatherOpenAPI/Interfaces/Response/BinaryResponse.swift index 0c4de85..c225f12 100644 --- a/Sources/FeatherOpenAPIKit/Interfaces/Response/BinaryResponse.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Response/BinaryResponse.swift @@ -3,17 +3,24 @@ import OpenAPIKit public protocol BinaryResponse: Response {} public extension BinaryResponse { + static func openAPIResponse() -> OpenAPI.Response { .init( + summary: nil, description: description, headers: openAPIHeaderMap(), content: openAPIContentMap() + [ .any: .init( - schema: .string( - contentMediaType: .other("application/octet-stream") + .init( + schema: .string( + contentMediaType: .other("application/octet-stream") + ), + examples: nil ) ) - ] + ], + links: .init(), + vendorExtensions: [:] ) } } diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Response/JSONResponse.swift b/Sources/FeatherOpenAPI/Interfaces/Response/JSONResponse.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Response/JSONResponse.swift rename to Sources/FeatherOpenAPI/Interfaces/Response/JSONResponse.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Response/Response.swift b/Sources/FeatherOpenAPI/Interfaces/Response/Response.swift similarity index 92% rename from Sources/FeatherOpenAPIKit/Interfaces/Response/Response.swift rename to Sources/FeatherOpenAPI/Interfaces/Response/Response.swift index e3e5283..43922cc 100644 --- a/Sources/FeatherOpenAPIKit/Interfaces/Response/Response.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Response/Response.swift @@ -32,7 +32,7 @@ public extension Response { static var headers: [Header.Type] { [] } static var contents: - [OpenAPIKit.OpenAPI.ContentType: FeatherOpenAPIKit.Schema.Type] + [OpenAPIKit.OpenAPI.ContentType: FeatherOpenAPI.Schema.Type] { [:] } @@ -48,7 +48,7 @@ public extension Response { static func openAPIContentMap() -> OpenAPI.Content.Map { var result: OpenAPI.Content.Map = [:] for (key, content) in contents { - result[key] = content.reference() + result[key] = .init(content.reference()) } return result } diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/ArraySchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/ArraySchema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/ArraySchema.swift rename to Sources/FeatherOpenAPI/Interfaces/Schema/ArraySchema.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/BooleanSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/BooleanSchema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/BooleanSchema.swift rename to Sources/FeatherOpenAPI/Interfaces/Schema/BooleanSchema.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/DateTimeSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/DateTimeSchema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/DateTimeSchema.swift rename to Sources/FeatherOpenAPI/Interfaces/Schema/DateTimeSchema.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/DoubleSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/DoubleSchema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/DoubleSchema.swift rename to Sources/FeatherOpenAPI/Interfaces/Schema/DoubleSchema.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/EmailSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/EmailSchema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/EmailSchema.swift rename to Sources/FeatherOpenAPI/Interfaces/Schema/EmailSchema.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/EnumSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/EnumSchema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/EnumSchema.swift rename to Sources/FeatherOpenAPI/Interfaces/Schema/EnumSchema.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/FloatSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/FloatSchema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/FloatSchema.swift rename to Sources/FeatherOpenAPI/Interfaces/Schema/FloatSchema.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/IDSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/IDSchema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/IDSchema.swift rename to Sources/FeatherOpenAPI/Interfaces/Schema/IDSchema.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/Int32Schema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/Int32Schema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/Int32Schema.swift rename to Sources/FeatherOpenAPI/Interfaces/Schema/Int32Schema.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/Int64Schema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/Int64Schema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/Int64Schema.swift rename to Sources/FeatherOpenAPI/Interfaces/Schema/Int64Schema.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/IntSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/IntSchema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/IntSchema.swift rename to Sources/FeatherOpenAPI/Interfaces/Schema/IntSchema.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/NanoIDSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/NanoIDSchema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/NanoIDSchema.swift rename to Sources/FeatherOpenAPI/Interfaces/Schema/NanoIDSchema.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/NumberSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/NumberSchema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/NumberSchema.swift rename to Sources/FeatherOpenAPI/Interfaces/Schema/NumberSchema.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/ObjectSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/ObjectSchema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/ObjectSchema.swift rename to Sources/FeatherOpenAPI/Interfaces/Schema/ObjectSchema.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/ObjectSchemaProperty.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/ObjectSchemaProperty.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/ObjectSchemaProperty.swift rename to Sources/FeatherOpenAPI/Interfaces/Schema/ObjectSchemaProperty.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/PasswordSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/PasswordSchema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/PasswordSchema.swift rename to Sources/FeatherOpenAPI/Interfaces/Schema/PasswordSchema.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/Schema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/Schema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/Schema.swift rename to Sources/FeatherOpenAPI/Interfaces/Schema/Schema.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/TextSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/TextSchema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/TextSchema.swift rename to Sources/FeatherOpenAPI/Interfaces/Schema/TextSchema.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/UUIDSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/UUIDSchema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/UUIDSchema.swift rename to Sources/FeatherOpenAPI/Interfaces/Schema/UUIDSchema.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/SecurityScheme/SecurityScheme.swift b/Sources/FeatherOpenAPI/Interfaces/SecurityScheme/SecurityScheme.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/SecurityScheme/SecurityScheme.swift rename to Sources/FeatherOpenAPI/Interfaces/SecurityScheme/SecurityScheme.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Tag/Tag.swift b/Sources/FeatherOpenAPI/Interfaces/Tag/Tag.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Tag/Tag.swift rename to Sources/FeatherOpenAPI/Interfaces/Tag/Tag.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Parameter/HeaderParameter.swift b/Sources/FeatherOpenAPIKit/Interfaces/Parameter/HeaderParameter.swift deleted file mode 100644 index c5d69c5..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Parameter/HeaderParameter.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 15/02/2024. -// - -import OpenAPIKit - -public protocol HeaderParameter: Parameter {} - -public extension HeaderParameter { - - static var context: OpenAPI.Parameter.Context { - .header(required: Self.required) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Parameter/QueryParameter.swift b/Sources/FeatherOpenAPIKit/Interfaces/Parameter/QueryParameter.swift deleted file mode 100644 index 9f7b014..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Parameter/QueryParameter.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - -import OpenAPIKit - -public protocol QueryParameter: Parameter {} - -public extension QueryParameter { - - static var required: Bool { false } - - static var context: OpenAPI.Parameter.Context { - .query(required: Self.required) - } -} diff --git a/Sources/FeatherOpenAPIKitMacros/FeatherOpenAPIKitMacros.swift b/Sources/FeatherOpenAPIKitMacros/FeatherOpenAPIKitMacros.swift deleted file mode 100644 index 65af380..0000000 --- a/Sources/FeatherOpenAPIKitMacros/FeatherOpenAPIKitMacros.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 26/01/2024. -// - -@attached(peer, names: arbitrary) -public macro ComponentCollection() = - #externalMacro( - module: "FeatherOpenAPIKitMacrosKit", - type: "ComponentCollectionMacro" - ) diff --git a/Sources/FeatherOpenAPIKitMacrosKit/FeatherOpenAPIKitMacrosKit.swift b/Sources/FeatherOpenAPIKitMacrosKit/FeatherOpenAPIKitMacrosKit.swift deleted file mode 100644 index 02c3774..0000000 --- a/Sources/FeatherOpenAPIKitMacrosKit/FeatherOpenAPIKitMacrosKit.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 26/01/2024. -// - -import SwiftCompilerPlugin -import SwiftSyntaxMacros - -@main -struct FeatherOpenAPIKitMacrosKit: CompilerPlugin { - - let providingMacros: [Macro.Type] = [ - ComponentCollectionMacro.self - ] -} diff --git a/Sources/FeatherOpenAPIKitMacrosKit/Macros/ComponentCollectionMacro.swift b/Sources/FeatherOpenAPIKitMacrosKit/Macros/ComponentCollectionMacro.swift deleted file mode 100644 index b6acb72..0000000 --- a/Sources/FeatherOpenAPIKitMacrosKit/Macros/ComponentCollectionMacro.swift +++ /dev/null @@ -1,100 +0,0 @@ -import SwiftCompilerPlugin -import SwiftSyntax -import SwiftSyntaxBuilder -import SwiftSyntaxMacros -import Foundation - -enum CustomError: Error { case message(String) } - -public struct ComponentCollectionMacro: PeerMacro { - private static func getTypeAndName(_ groupType: String) throws -> ( - variable: String, type: String - ) { - switch groupType { - case "Schemas": - return ("schemas", "Schema") - case "Parameters": - return ("parameters", "Parameter") - case "Headers": - return ("headers", "Header") - case "RequestBodies": - return ("requestBodies", "RequestBody") - case "SecuritySchemes": - return ("securitySchemes", "SecurityScheme") - case "Responses": - return ("responses", "Response") - case "Tags": - return ("tags", "Tag") - case "Operations": - return ("operations", "Operation") - case "PathItems": - return ("pathItems", "PathItem") - default: - throw CustomError.message("Invalid enum name: \(groupType)") - } - } - - private static func collectEnumTypes( - _ parentNodeName: String, - _ node: MemberBlockSyntax - ) -> [String] { - var ret: [String] = [] - - for member in node.members { - if let enumDecl = member.decl.as(EnumDeclSyntax.self) { - let enumName = parentNodeName + "." + enumDecl.name.text - - ret += collectEnumTypes(enumName, enumDecl.memberBlock) - - if let _ = enumDecl.inheritanceClause { - ret.append(enumName + ".self") - } - } - else if let structDecl = member.decl.as(StructDeclSyntax.self) { - ret += collectEnumTypes( - parentNodeName + "." + structDecl.name.text, - structDecl.memberBlock - ) - } - else if let classDecl = member.decl.as(ClassDeclSyntax.self) { - ret += collectEnumTypes( - parentNodeName + "." + classDecl.name.text, - classDecl.memberBlock - ) - } - } - - return ret - } - - public static func expansion( - of node: SwiftSyntax.AttributeSyntax, - providingPeersOf declaration: some SwiftSyntax.DeclSyntaxProtocol, - in context: some SwiftSyntaxMacros.MacroExpansionContext - ) throws -> [SwiftSyntax.DeclSyntax] { - - guard let enumDecl = declaration.as(EnumDeclSyntax.self) else { - throw CustomError.message( - "This macro can only be applied to an enum." - ) - } - - let groupType = enumDecl.name.text - let nameAndType = try getTypeAndName(groupType) - let collectedMemberTypes = collectEnumTypes( - groupType, - enumDecl.memberBlock - ) - .joined(separator: ",\n") - - let extended = DeclSyntax( - """ - public static let \(raw: nameAndType.variable) : [\(raw: nameAndType.type).Type] = [ - \(raw: collectedMemberTypes) - ] - """ - ) - - return [extended] - } -} diff --git a/Sources/feather-openapi-generator/feather-openapi-generator.swift b/Sources/feather-openapi-generator/feather-openapi-generator.swift index 3cda871..e5ef18f 100644 --- a/Sources/feather-openapi-generator/feather-openapi-generator.swift +++ b/Sources/feather-openapi-generator/feather-openapi-generator.swift @@ -35,7 +35,7 @@ struct _Tool { let code = """ //generated on: \(dateString) - \(target == "FeatherOpenAPIKit" ? "" : "import FeatherOpenAPIKit") + \(target == "FeatherOpenAPI" ? "" : "import FeatherOpenAPI") extension Component { @@ -146,7 +146,10 @@ struct _Tool { } else if fileURL.pathExtension == "swift" { do { - let fileContent = try String(contentsOf: fileURL) + let fileContent = try String( + contentsOf: fileURL, + encoding: .utf8 + ) let list = collectTypes( Parser.parse(source: fileContent) ) diff --git a/Tests/FeatherOpenAPIKitMacrosKitTests/FeatherOpenAPIKitMacrosTests.swift b/Tests/FeatherOpenAPIKitMacrosKitTests/FeatherOpenAPIKitMacrosTests.swift deleted file mode 100644 index e0c50fb..0000000 --- a/Tests/FeatherOpenAPIKitMacrosKitTests/FeatherOpenAPIKitMacrosTests.swift +++ /dev/null @@ -1,96 +0,0 @@ -import SwiftSyntaxMacros -import SwiftSyntaxMacrosTestSupport -import XCTest -import FeatherOpenAPIKitMacrosKit - -final class FeatherOpenAPIKitMacrosTests: XCTestCase { - - let testMacros: [String: Macro.Type] = [ - "ComponentCollection": ComponentCollectionMacro.self - ] - - func testProtoMacro() { - //TODO: make test - assertMacroExpansion( - """ - import FeatherOpenAPIKit - import OpenAPIKit - - extension Example.Model { - @ComponentCollection - enum Schemas { - enum empty { - } - - enum Id: UUIDSchema { - static let description = "Unique example model identifier" - } - - enum Key { - enum InsiderKey: TextSchema { - enum InsiderKeyLevel3: TextSchema { - enum InsiderKeyLevel4 { - enum InsiderKeyLevel5: TextSchema { - } - } - } - static let description = "Key of the example model" - static let examples = [ - "my-example-key", - ] - } - - static let description = "Key of the example model" - static let examples = [ - "my-example-key", - ] - } - } - } - """, - expandedSource: """ - import FeatherOpenAPIKit - import OpenAPIKit - - extension Example.Model { - enum Schemas { - enum empty { - } - - enum Id: UUIDSchema { - static let description = "Unique example model identifier" - } - - enum Key { - enum InsiderKey: TextSchema { - enum InsiderKeyLevel3: TextSchema { - enum InsiderKeyLevel4 { - enum InsiderKeyLevel5: TextSchema { - } - } - } - static let description = "Key of the example model" - static let examples = [ - "my-example-key", - ] - } - - static let description = "Key of the example model" - static let examples = [ - "my-example-key", - ] - } - } - - public static let schemas : [Schema.Type] = [ - Schemas.Id.self, - Schemas.Key.InsiderKey.InsiderKeyLevel3.InsiderKeyLevel4.InsiderKeyLevel5.self, - Schemas.Key.InsiderKey.InsiderKeyLevel3.self, - Schemas.Key.InsiderKey.self - ] - } - """, - macros: testMacros - ) - } -} diff --git a/Tests/FeatherOpenAPIKitMacrosTests/FeatherOpenAPIKitMacrosTests.swift b/Tests/FeatherOpenAPIKitMacrosTests/FeatherOpenAPIKitMacrosTests.swift deleted file mode 100644 index 21716ac..0000000 --- a/Tests/FeatherOpenAPIKitMacrosTests/FeatherOpenAPIKitMacrosTests.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 26/01/2024. -// - -import XCTest -import FeatherOpenAPIKit -import FeatherOpenAPIKitMacros - -final class FeatherOpenAPIKitMacrosTests: XCTestCase { - - func testExample() { - enum TopLevel { - @ComponentCollection - enum Schemas { - enum Foo { - enum Bar: TextSchema { - enum empty { - } - - enum Baz: TextSchema { - static let description = "" - static let examples = ["baz"] - } - static let description = "" - static let examples = ["bar"] - } - } - } - } - - XCTAssert(TopLevel.schemas.count == 2) - // wtf??? why does not work this test properly? (when I change it for 3 it will not produce error but it should do) - //XCTAssertEqual(TopLevel.schemas.count, 3) - } -} diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Operations.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Operations.swift deleted file mode 100644 index 3dffe9e..0000000 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Operations.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - -import FeatherOpenAPIKit - -extension Example.Model { - - static let operations: [Operation.Type] = [ - Operations.Get.self, - Operations.Create.self, - ] - - enum Operations { - - enum Get: Operation { - static let tag: Tag.Type = Tags.Main.self - static let summary = "Detail example" - static let description = "Detail example detail" - static var parameters: [Parameter.Type] = [ - // Parameters.Id.self, - Parameters.CustomRequestHeader.self - ] - static let responses: [OperationResponse] = [ - .init(200, Responses.Detail.self) - ] - } - - enum Create: Operation { - static let tag: Tag.Type = Tags.Main.self - static let summary = "Create example" - static let description = "Create example detail" - static var requestBody: RequestBody.Type? = RequestBodies.Create - .self - static let responses: [OperationResponse] = [ - .init(200, Responses.Detail.self) - ] - static let security: [SecurityScheme.Type] = [ - SecuritySchemes.BearerToken.self - ] - } - - } -} diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Example.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Example.swift similarity index 100% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Example.swift rename to Tests/FeatherOpenAPITests/Example/Components/Example/Example.swift diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Headers.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Headers.swift similarity index 94% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Headers.swift rename to Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Headers.swift index caaa4b1..e4048e0 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Headers.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Headers.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension Example.Model { diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift new file mode 100644 index 0000000..3192fe2 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift @@ -0,0 +1,55 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 20/01/2024. +// + +import FeatherOpenAPI + +extension Example.Model { + + static let operations: [Operation.Type] = [ + Operations.Get.self, + Operations.Create.self, + ] + + enum Operations { + + enum Get: Operation { + static let ffff: String = "asdf" + static let tag: Tag.Type = Tags.Main.self + static let summary = "Detail example" + static let description = "Detail example detail" + static var parameters: [Parameter.Type] { [ + // Parameters.Id.self, + Parameters.CustomRequestHeader.self + ] + } + static let responses: [OperationResponse] = [ + .init(200, Responses.Detail.self) + ] + } + + enum Create: Operation { + + static var tag: Tag.Type { Tags.Main.self } + static var summary: String { "Create example" } + static var description: String { "Create example detail" } + static var requestBody: RequestBody.Type? { + RequestBodies.Create.self + } + static var responses: [OperationResponse] { + [ + .init(200, Responses.Detail.self) + ] + } + static var security: [SecurityScheme.Type] { + [ + SecuritySchemes.BearerToken.self + ] + } + } + + } +} diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Parameters.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Parameters.swift similarity index 59% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Parameters.swift rename to Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Parameters.swift index dfd2c58..0b9cda4 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Parameters.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Parameters.swift @@ -5,27 +5,29 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension Example.Model { - static let parameters: [Parameter.Type] = [ - Parameters.Id.self, - Parameters.CustomRequestHeader.self, - ] + static var parameters: [Parameter.Type] { + [ + Parameters.Id.self, + Parameters.CustomRequestHeader.self, + ] + } enum Parameters { enum Id: PathParameter { static let name = "id" static let description = "Example parameter" - static let schema: Schema.Type = Schemas.Id.self + static var schema: Schema.Type { Schemas.Id.self } } enum CustomRequestHeader: HeaderParameter { static let name = "CustomRequestHeader" static let description = "Example request header parameter" - static let schema: Schema.Type = Schemas.CustomHeader.self + static var schema: Schema.Type { Schemas.CustomHeader.self } } } } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+PathItems.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+PathItems.swift similarity index 96% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+PathItems.swift rename to Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+PathItems.swift index 2e61c74..7f31e36 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+PathItems.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+PathItems.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension Example.Model { diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift similarity index 93% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift rename to Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift index 7b8c0fc..0b566cd 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension Example.Model { diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Responses.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Responses.swift similarity index 96% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Responses.swift rename to Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Responses.swift index 6c54167..5cc4990 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Responses.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Responses.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI import OpenAPIKit extension Example.Model { diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Schemas.swift similarity index 54% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Schemas.swift rename to Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Schemas.swift index a6da844..1316820 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Schemas.swift @@ -5,21 +5,23 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension Example.Model { - static let schemas: [Schema.Type] = [ - Schemas.Id.self, - Schemas.Key.self, - Schemas.Create.self, - Schemas.Patch.self, - Schemas.Detail.self, - Schemas.List.self, - Schemas.List.Item.self, - Schemas.CustomHeader.self, - Schemas.PatchOverride.self, - ] + static var schemas: [Schema.Type] { + [ + Schemas.Id.self, + Schemas.Key.self, + Schemas.Create.self, + Schemas.Patch.self, + Schemas.Detail.self, + Schemas.List.self, + Schemas.List.Item.self, + Schemas.CustomHeader.self, + Schemas.PatchOverride.self, + ] + } enum Schemas { @@ -43,26 +45,32 @@ extension Example.Model { enum Create: ObjectSchema { static let description = "example model create object" - static let properties: [ObjectSchemaProperty] = [ - .init("key", Key.self) - ] + static var properties: [ObjectSchemaProperty] { + [ + .init("key", Key.self) + ] + } } enum Detail: ObjectSchema { static let description = "example model detail object" - static let properties: [ObjectSchemaProperty] = [ - .init("id", Id.self), - .init("key", Key.self), - ] + static var properties: [ObjectSchemaProperty] { + [ + .init("id", Id.self), + .init("key", Key.self), + ] + } } enum Patch: ObjectSchema { static let description = "example model detail object" - static let properties: [ObjectSchemaProperty] = [ - .init("key", Key.self, required: false) - ] + static var properties: [ObjectSchemaProperty] { + [ + .init("key", Key.self, required: false) + ] + } } enum List: ArraySchema { @@ -70,14 +78,16 @@ extension Example.Model { enum Item: ObjectSchema { static let description = "example model detail object" - static let properties: [ObjectSchemaProperty] = [ - .init("id", Id.self), - .init("key", Key.self), - ] + static var properties: [ObjectSchemaProperty] { + [ + .init("id", Id.self), + .init("key", Key.self), + ] + } } static let description = "Lorem ipsum dolor sit amet" - static let items: Schema.Type = Item.self + static var items: Schema.Type { Item.self } } enum PatchOverride: ObjectSchema { @@ -86,9 +96,11 @@ extension Example.Model { static let description = "overridden" - static let properties: [ObjectSchemaProperty] = [ - .init("key", Key.self, required: false) - ] + static var properties: [ObjectSchemaProperty] { + [ + .init("key", Key.self, required: false) + ] + } } } } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift similarity index 97% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift rename to Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift index 27e40c2..b2a73e2 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI import OpenAPIKit // shared operation security diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Tags.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Tags.swift similarity index 91% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Tags.swift rename to Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Tags.swift index bf75f33..34fdd52 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Tags.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Tags.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension Example.Model { diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel.swift similarity index 83% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel.swift rename to Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel.swift index aa8f8a1..0c06cf1 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 25/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension Example { diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/ExampleDuplicatedItem.swift b/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/ExampleDuplicatedItem.swift similarity index 100% rename from Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/ExampleDuplicatedItem.swift rename to Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/ExampleDuplicatedItem.swift diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift similarity index 96% rename from Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift rename to Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift index 9751b50..9ac6457 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension ExampleDuplicatedItem.Model { diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift b/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift similarity index 84% rename from Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift rename to Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift index 5582609..1bd9b57 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 25/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension ExampleDuplicatedItem { diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/ExampleMissingParentItem.swift b/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/ExampleMissingParentItem.swift similarity index 100% rename from Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/ExampleMissingParentItem.swift rename to Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/ExampleMissingParentItem.swift diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift similarity index 95% rename from Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift rename to Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift index dc80984..017db73 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension ExampleMissingParentItem.Model { diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift b/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift similarity index 84% rename from Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift rename to Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift index 1bc4f14..0628156 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 25/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension ExampleMissingParentItem { diff --git a/Tests/FeatherOpenAPIKitTests/Example/ExampleDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift similarity index 97% rename from Tests/FeatherOpenAPIKitTests/Example/ExampleDocument.swift rename to Tests/FeatherOpenAPITests/Example/ExampleDocument.swift index 85d45dc..0b8aa61 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/ExampleDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI import OpenAPIKit struct ExampleDocument: Document { diff --git a/Tests/FeatherOpenAPIKitTests/Example/ExampleDuplicatedItemDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift similarity index 97% rename from Tests/FeatherOpenAPIKitTests/Example/ExampleDuplicatedItemDocument.swift rename to Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift index 001c8ec..e2bc4dd 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/ExampleDuplicatedItemDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI import OpenAPIKit struct ExampleDuplicatedItemDocument: Document { diff --git a/Tests/FeatherOpenAPIKitTests/Example/ExampleMissingParentItemDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift similarity index 97% rename from Tests/FeatherOpenAPIKitTests/Example/ExampleMissingParentItemDocument.swift rename to Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift index 1ede020..236da83 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/ExampleMissingParentItemDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI import OpenAPIKit struct ExampleMissingParentItemItemDocument: Document { diff --git a/Tests/FeatherOpenAPIKitTests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift similarity index 98% rename from Tests/FeatherOpenAPIKitTests/FeatherOpenAPIKitTests.swift rename to Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift index 955a83c..79369a5 100644 --- a/Tests/FeatherOpenAPIKitTests/FeatherOpenAPIKitTests.swift +++ b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift @@ -11,7 +11,7 @@ import OpenAPIKitCore import Yams import XCTest -@testable import FeatherOpenAPIKit +@testable import FeatherOpenAPI final class FeatherOpenAPIKitTests: XCTestCase { diff --git a/Tests/FeatherOpenAPIKitTests/PathComponentTests.swift b/Tests/FeatherOpenAPITests/PathComponentTests.swift similarity index 98% rename from Tests/FeatherOpenAPIKitTests/PathComponentTests.swift rename to Tests/FeatherOpenAPITests/PathComponentTests.swift index 9bbc103..73175e2 100644 --- a/Tests/FeatherOpenAPIKitTests/PathComponentTests.swift +++ b/Tests/FeatherOpenAPITests/PathComponentTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest -@testable import FeatherOpenAPIKit +@testable import FeatherOpenAPI fileprivate extension Path { diff --git a/docker/tests/Dockerfile b/docker/tests/Dockerfile new file mode 100644 index 0000000..73be175 --- /dev/null +++ b/docker/tests/Dockerfile @@ -0,0 +1,11 @@ +FROM swift:6.1 + +WORKDIR /app + +COPY . ./ + +RUN swift package resolve +RUN swift package clean +RUN swift package update + +CMD ["swift", "test", "--parallel", "--enable-code-coverage"] From fc41c0c71b02879e06c3c4a1c8dadcaff1a86223 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Tue, 20 Jan 2026 20:06:08 +0100 Subject: [PATCH 03/34] fix plugin warnings --- ...penAPIGenerator.swift => Entrypoint.swift} | 107 ++++++++++-------- 1 file changed, 58 insertions(+), 49 deletions(-) rename Plugins/FeatherOpenAPIGenerator/{FeatherOpenAPIGenerator.swift => Entrypoint.swift} (54%) diff --git a/Plugins/FeatherOpenAPIGenerator/FeatherOpenAPIGenerator.swift b/Plugins/FeatherOpenAPIGenerator/Entrypoint.swift similarity index 54% rename from Plugins/FeatherOpenAPIGenerator/FeatherOpenAPIGenerator.swift rename to Plugins/FeatherOpenAPIGenerator/Entrypoint.swift index 6c71870..7b3a585 100644 --- a/Plugins/FeatherOpenAPIGenerator/FeatherOpenAPIGenerator.swift +++ b/Plugins/FeatherOpenAPIGenerator/Entrypoint.swift @@ -1,39 +1,82 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif + import PackagePlugin enum PluginError: Error { case noTarget(name: String) } -@main struct FeatherOpenAPIGenerator { - static func createBuildCommands( - pluginWorkDirectory: Path, +@main +struct Entrypoint: BuildToolPlugin { + + func createBuildCommands( + context: PluginContext, + target: Target + ) async throws -> [Command] { + guard let swiftTarget = target as? SwiftSourceModuleTarget else { + throw PluginError.noTarget(name: target.name) + } + let commandBuilder = CommandBuilder() + return try commandBuilder.createBuildCommands( + pluginWorkDirectoryURL: context.pluginWorkDirectoryURL, + tool: context.tool, + sourceFiles: swiftTarget.sourceFiles, + targetName: target.name + ) + } +} + +#if canImport(XcodeProjectPlugin) +import XcodeProjectPlugin + +extension Entrypoint: XcodeBuildToolPlugin { + + func createBuildCommands( + context: XcodePluginContext, + target: XcodeTarget + ) throws -> [Command] { + let commandBuilder = CommandBuilder() + return try commandBuilder.createBuildCommands( + pluginWorkDirectoryURL: context.pluginWorkDirectoryURL, + tool: context.tool, + sourceFiles: target.inputFiles, + targetName: target.displayName + ) + } +} +#endif + + +struct CommandBuilder { + + func createBuildCommands( + pluginWorkDirectoryURL: URL, tool: (String) throws -> PluginContext.Tool, sourceFiles: FileList, targetName: String ) throws -> [Command] { - let sourceDir = longestCommonFolderPath(sourceFiles.map(\.path.string)) - let output = pluginWorkDirectory.appending( - "Component+Generated.swift" - ) + let sourceDir = longestCommonFolderPath(sourceFiles.map { $0.url.path() }) + let output = pluginWorkDirectoryURL.appending(path: "Component+Generated.swift") return [ .buildCommand( displayName: "Generate component extension code", - executable: try tool( - "feather-openapi-generator" - ) - .path, - arguments: [sourceDir, output, targetName], + executable: try tool("feather-openapi-generator").url, + arguments: [sourceDir, output.path(), targetName], environment: [:], inputFiles: [], outputFiles: [output] - ) + ), ] } - private static func longestCommonFolderPath(_ filePaths: [String]) -> String - { + func longestCommonFolderPath( + _ filePaths: [String] + ) -> String { guard !filePaths.isEmpty else { return "" } var commonComponents = filePaths.first!.components(separatedBy: "/") @@ -51,37 +94,3 @@ enum PluginError: Error { return commonComponents.joined(separator: "/") } } - -extension FeatherOpenAPIGenerator: BuildToolPlugin { - func createBuildCommands(context: PluginContext, target: Target) - async throws -> [Command] - { - guard let swiftTarget = target as? SwiftSourceModuleTarget else { - throw PluginError.noTarget(name: target.name) - } - return try Self.createBuildCommands( - //targetDirectory: target.directory, - pluginWorkDirectory: context.pluginWorkDirectory, - tool: context.tool, - sourceFiles: swiftTarget.sourceFiles, - targetName: target.name - ) - } -} - -#if canImport(XcodeProjectPlugin) -import XcodeProjectPlugin - -extension FeatherOpenAPIGenerator: XcodeBuildToolPlugin { - func createBuildCommands(context: XcodePluginContext, target: XcodeTarget) - throws -> [Command] - { - try Self.createBuildCommands( - pluginWorkDirectory: context.pluginWorkDirectory, - tool: context.tool, - sourceFiles: target.inputFiles, - targetName: target.displayName - ) - } -} -#endif From 18cfdfb61df44c11c8fd9d65da3c236c827252c9 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Tue, 20 Jan 2026 20:13:23 +0100 Subject: [PATCH 04/34] minor changes --- .swiftformatignore | 1 + .../CommandBuilder.swift | 62 +++++++++++++ .../FeatherOpenAPIGenerator/Entrypoint.swift | 49 +---------- .../FeatherOpenAPI/Interfaces/Document.swift | 17 ++-- .../Interfaces/Identifiable.swift | 3 +- .../Interfaces/Parameter/Parameter.swift | 4 +- .../Interfaces/RequestBody/BinaryBody.swift | 2 +- .../Interfaces/Response/BinaryResponse.swift | 2 +- .../Entrypoint.swift | 71 +++++++++++++++ ...pi-generator.swift => TypeCollector.swift} | 88 ++++++------------- .../Model/ExampleModel+Operations.swift | 9 +- 11 files changed, 184 insertions(+), 124 deletions(-) create mode 100644 .swiftformatignore create mode 100644 Plugins/FeatherOpenAPIGenerator/CommandBuilder.swift create mode 100644 Sources/feather-openapi-generator/Entrypoint.swift rename Sources/feather-openapi-generator/{feather-openapi-generator.swift => TypeCollector.swift} (61%) diff --git a/.swiftformatignore b/.swiftformatignore new file mode 100644 index 0000000..c73cf0c --- /dev/null +++ b/.swiftformatignore @@ -0,0 +1 @@ +Package.swift \ No newline at end of file diff --git a/Plugins/FeatherOpenAPIGenerator/CommandBuilder.swift b/Plugins/FeatherOpenAPIGenerator/CommandBuilder.swift new file mode 100644 index 0000000..585b400 --- /dev/null +++ b/Plugins/FeatherOpenAPIGenerator/CommandBuilder.swift @@ -0,0 +1,62 @@ +// +// CommandBuilder.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 20.. +// + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +import PackagePlugin + +struct CommandBuilder { + + func createBuildCommands( + pluginWorkDirectoryURL: URL, + tool: (String) throws -> PluginContext.Tool, + sourceFiles: FileList, + targetName: String + ) throws -> [Command] { + let sourceDir = longestCommonFolderPath( + sourceFiles.map { $0.url.path() } + ) + let output = pluginWorkDirectoryURL.appending( + path: "Component+Generated.swift" + ) + + return [ + .buildCommand( + displayName: "Generate component extension code", + executable: try tool("feather-openapi-generator").url, + arguments: [sourceDir, output.path(), targetName], + environment: [:], + inputFiles: [], + outputFiles: [output] + ) + ] + } + + func longestCommonFolderPath( + _ filePaths: [String] + ) -> String { + guard !filePaths.isEmpty else { return "" } + + var commonComponents = filePaths.first!.components(separatedBy: "/") + commonComponents.removeLast() + + for path in filePaths { + let components = path.components(separatedBy: "/") + for i in 0.. PluginContext.Tool, - sourceFiles: FileList, - targetName: String - ) throws -> [Command] { - let sourceDir = longestCommonFolderPath(sourceFiles.map { $0.url.path() }) - let output = pluginWorkDirectoryURL.appending(path: "Component+Generated.swift") - - return [ - .buildCommand( - displayName: "Generate component extension code", - executable: try tool("feather-openapi-generator").url, - arguments: [sourceDir, output.path(), targetName], - environment: [:], - inputFiles: [], - outputFiles: [output] - ), - ] - } - - func longestCommonFolderPath( - _ filePaths: [String] - ) -> String { - guard !filePaths.isEmpty else { return "" } - - var commonComponents = filePaths.first!.components(separatedBy: "/") - commonComponents.removeLast() - - for path in filePaths { - let components = path.components(separatedBy: "/") - for i in 0.. OpenAPI.ComponentReferenceDictionary { + func parameters() throws + -> OpenAPI.ComponentReferenceDictionary + { return try Self.filterIdentifiables( lists: components.map { @@ -93,7 +95,9 @@ public extension Document { } } - func headers() throws -> OpenAPI.ComponentReferenceDictionary { + func headers() throws + -> OpenAPI.ComponentReferenceDictionary + { return try Self.filterIdentifiables( lists: components.map { @@ -105,7 +109,8 @@ public extension Document { } } - func requestBodies() throws -> OpenAPI.ComponentReferenceDictionary + func requestBodies() throws + -> OpenAPI.ComponentReferenceDictionary { return try Self.filterIdentifiables( @@ -132,8 +137,10 @@ public extension Document { } } - func responses() throws -> OpenAPI.ComponentReferenceDictionary { - + func responses() throws + -> OpenAPI.ComponentReferenceDictionary + { + return try Self.filterIdentifiables( lists: components.map { diff --git a/Sources/FeatherOpenAPI/Interfaces/Identifiable.swift b/Sources/FeatherOpenAPI/Interfaces/Identifiable.swift index 0605914..274fed4 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Identifiable.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Identifiable.swift @@ -18,7 +18,8 @@ public extension Identifiable { var components = String(reflecting: self).split(separator: ".") components.remove(at: 0) // remove namespace components.remove(at: 2) // remove enum name - return components + return + components .joined(separator: "") .replacing("GenericComponent", with: "Generic") } diff --git a/Sources/FeatherOpenAPI/Interfaces/Parameter/Parameter.swift b/Sources/FeatherOpenAPI/Interfaces/Parameter/Parameter.swift index 5d79b3f..e858692 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Parameter/Parameter.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Parameter/Parameter.swift @@ -38,8 +38,8 @@ public extension Parameter { static func openAPIParameter() -> OpenAPI.Parameter { .init( name: name, - context: context, // TODO: fix me -// schema: schema.reference(), + context: context, // TODO: fix me + // schema: schema.reference(), description: description ) } diff --git a/Sources/FeatherOpenAPI/Interfaces/RequestBody/BinaryBody.swift b/Sources/FeatherOpenAPI/Interfaces/RequestBody/BinaryBody.swift index ea6363a..bfdb2c2 100644 --- a/Sources/FeatherOpenAPI/Interfaces/RequestBody/BinaryBody.swift +++ b/Sources/FeatherOpenAPI/Interfaces/RequestBody/BinaryBody.swift @@ -21,7 +21,7 @@ extension BinaryBody { ), examples: nil ) - ), + ) ], required: required ) diff --git a/Sources/FeatherOpenAPI/Interfaces/Response/BinaryResponse.swift b/Sources/FeatherOpenAPI/Interfaces/Response/BinaryResponse.swift index c225f12..45971d3 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Response/BinaryResponse.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Response/BinaryResponse.swift @@ -3,7 +3,7 @@ import OpenAPIKit public protocol BinaryResponse: Response {} public extension BinaryResponse { - + static func openAPIResponse() -> OpenAPI.Response { .init( summary: nil, diff --git a/Sources/feather-openapi-generator/Entrypoint.swift b/Sources/feather-openapi-generator/Entrypoint.swift new file mode 100644 index 0000000..5c39319 --- /dev/null +++ b/Sources/feather-openapi-generator/Entrypoint.swift @@ -0,0 +1,71 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +import SwiftParser +import SwiftSyntax + +enum BuilderError: Error { + case wrongArgumentsNumber + case invalidOutputFormat + case errorReadingContent(String) + case errorAccessContent(String) +} + +@main +struct Entrypoint { + + static func main() async throws { + print("feather-openapi-generator is running...") + + guard CommandLine.arguments.count >= 3 else { + throw BuilderError.wrongArgumentsNumber + } + + let input = URL(fileURLWithPath: CommandLine.arguments[1]) + let output = URL(fileURLWithPath: CommandLine.arguments[2]) + let target = + CommandLine.arguments.count > 3 ? CommandLine.arguments[3] : "" + + let typeCollector = TypeCollector() + let typeList = try typeCollector.collectTypes(input.path) + let collectedTypes = typeList.joined(separator: ",\n ") + + let dateString = DateFormatter.localizedString( + from: Date(), + dateStyle: .medium, + timeStyle: .medium + ) + + let code = + """ + // generated on: \(dateString) + \(target == "FeatherOpenAPI" ? "" : "import FeatherOpenAPI") + + extension Component { + + public static func getComponentsOfType() -> [T] { + let prefixName = String(reflecting: self) + "." + return [ + \(collectedTypes) + ] + .compactMap { $0 as? T }.filter { + String(reflecting: $0).hasPrefix(prefixName) + } + } + } + """ + + print("Generated code path: \(output.path)") + + guard let data = code.data(using: .utf8) else { + throw BuilderError.invalidOutputFormat + } + + try data.write(to: output, options: .atomic) + + print("feather-openapi-generator finished.") + } +} diff --git a/Sources/feather-openapi-generator/feather-openapi-generator.swift b/Sources/feather-openapi-generator/TypeCollector.swift similarity index 61% rename from Sources/feather-openapi-generator/feather-openapi-generator.swift rename to Sources/feather-openapi-generator/TypeCollector.swift index e5ef18f..899db48 100644 --- a/Sources/feather-openapi-generator/feather-openapi-generator.swift +++ b/Sources/feather-openapi-generator/TypeCollector.swift @@ -1,71 +1,28 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 20.. +// + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif + import SwiftParser import SwiftSyntax -enum BuilderError: Error { - case wrongArgumentsNumber - case invalidOutputFormat - case errorReadingContent(String) - case errorAccessContent(String) -} - -@main -struct _Tool { - static func main() async throws { - print("FeatherOpenAPIGenerator is running...") - - guard CommandLine.arguments.count >= 3 else { - throw BuilderError.wrongArgumentsNumber - } - - let input = URL(fileURLWithPath: CommandLine.arguments[1]) - let output = URL(fileURLWithPath: CommandLine.arguments[2]) - let target = - CommandLine.arguments.count > 3 ? CommandLine.arguments[3] : "" - - let typeList = try collectTypes(input.path) - let collectedTypes = typeList.joined(separator: ",\n ") - - let dateString = DateFormatter.localizedString( - from: Date(), - dateStyle: .medium, - timeStyle: .medium - ) - - let code = - """ - //generated on: \(dateString) - \(target == "FeatherOpenAPI" ? "" : "import FeatherOpenAPI") - - extension Component { - - public static func getComponentsOfType() -> [T] { - let prefixName = String(reflecting: self) + "." - return [ - \(collectedTypes) - ].compactMap { $0 as? T }.filter { - String(reflecting: $0).hasPrefix(prefixName) - } - } - } - """ - - print("Generated code path: \(output.path)") - - guard let data = code.data(using: .utf8) else { - throw BuilderError.invalidOutputFormat - } +struct TypeCollector { - try data.write(to: output, options: .atomic) - - print("FeatherOpenAPIGenerator finished.") - } - - private static func getFullName(_ node: TypeSyntax) -> String { + func getFullName( + _ node: TypeSyntax + ) -> String { node.trimmedDescription } - private static func getNodeName( + func getNodeName( _ parentNodeName: String, _ nodeName: String ) -> String { @@ -75,7 +32,7 @@ struct _Tool { return parentNodeName + "." + nodeName } - private static func collectTypes( + func collectTypes( _ parentNodeName: String, _ node: SyntaxProtocol ) -> [String] { @@ -115,7 +72,9 @@ struct _Tool { return ret } - private static func collectTypes(_ root: SourceFileSyntax) -> [String] { + func collectTypes( + _ root: SourceFileSyntax + ) -> [String] { var ret: [String] = [] for st in root.statements { @@ -126,7 +85,9 @@ struct _Tool { return ret } - private static func collectTypes(_ dirPath: String) throws -> [String] { + func collectTypes( + _ dirPath: String + ) throws -> [String] { var ret: [String] = [] let fileManager = FileManager.default @@ -171,4 +132,5 @@ struct _Tool { return ret } + } diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift index 3192fe2..59d8a12 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift @@ -21,10 +21,11 @@ extension Example.Model { static let tag: Tag.Type = Tags.Main.self static let summary = "Detail example" static let description = "Detail example detail" - static var parameters: [Parameter.Type] { [ - // Parameters.Id.self, - Parameters.CustomRequestHeader.self - ] + static var parameters: [Parameter.Type] { + [ + // Parameters.Id.self, + Parameters.CustomRequestHeader.self + ] } static let responses: [OperationResponse] = [ .init(200, Responses.Detail.self) From 2e35ea5516176a779ce55df1d9132f8552c12243 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Tue, 20 Jan 2026 20:23:08 +0100 Subject: [PATCH 05/34] other fixes --- .../FeatherOpenAPI/Interfaces/Document.swift | 14 +++-- .../Example/Model/ExampleModel+Headers.swift | 10 +-- .../Model/ExampleModel+Operations.swift | 20 +++--- .../Model/ExampleModel+PathItems.swift | 28 +++++---- .../Model/ExampleModel+RequestBodies.swift | 10 +-- .../Model/ExampleModel+Responses.swift | 26 +++++--- .../Model/ExampleModel+SecuritySchemes.swift | 8 ++- .../Example/Model/ExampleModel+Tags.swift | 8 ++- .../ExampleDuplicatedItemModel+Schemas.swift | 12 ++-- ...xampleMissingParentItemModel+Schemas.swift | 10 +-- .../Example/ExampleDocument.swift | 1 + .../ExampleDuplicatedItemDocument.swift | 1 + .../ExampleMissingParentItemDocument.swift | 1 + .../FeatherOpenAPIKitTests.swift | 38 ++++++----- .../PathComponentTests.swift | 63 +++++++++++-------- 15 files changed, 151 insertions(+), 99 deletions(-) diff --git a/Sources/FeatherOpenAPI/Interfaces/Document.swift b/Sources/FeatherOpenAPI/Interfaces/Document.swift index 3316483..cea95db 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Document.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Document.swift @@ -15,12 +15,16 @@ public protocol Document { func openAPIDocument() throws -> OpenAPI.Document func schemas() throws -> OpenAPI.ComponentDictionary - func parameters() throws -> OpenAPI.ComponentDictionary - func headers() throws -> OpenAPI.ComponentDictionary - func requestBodies() throws -> OpenAPI.ComponentDictionary + func parameters() throws + -> OpenAPI.ComponentReferenceDictionary + func headers() throws + -> OpenAPI.ComponentReferenceDictionary + func requestBodies() throws + -> OpenAPI.ComponentReferenceDictionary func securitySchemes() throws - -> OpenAPI.ComponentDictionary - func responses() throws -> OpenAPI.ComponentDictionary + -> OpenAPI.ComponentReferenceDictionary + func responses() throws + -> OpenAPI.ComponentReferenceDictionary func tags() throws -> [OpenAPI.Tag] func paths() throws -> OpenAPI.PathItem.Map diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Headers.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Headers.swift index e4048e0..01388d5 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Headers.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Headers.swift @@ -9,16 +9,18 @@ import FeatherOpenAPI extension Example.Model { - static let headers: [Header.Type] = [ - Headers.CustomResponseHeader.self - ] + static var headers: [Header.Type] { + [ + Headers.CustomResponseHeader.self + ] + } enum Headers { enum CustomResponseHeader: Header { static let name = "X-Custom-Response-Header" static let description: String = "My custom response header" - static var schema: Schema.Type = Schemas.CustomHeader.self + static var schema: Schema.Type { Schemas.CustomHeader.self } } } } diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift index 59d8a12..db3ee62 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift @@ -9,16 +9,18 @@ import FeatherOpenAPI extension Example.Model { - static let operations: [Operation.Type] = [ - Operations.Get.self, - Operations.Create.self, - ] + static var operations: [Operation.Type] { + [ + Operations.Get.self, + Operations.Create.self, + ] + } enum Operations { enum Get: Operation { static let ffff: String = "asdf" - static let tag: Tag.Type = Tags.Main.self + static var tag: Tag.Type { Tags.Main.self } static let summary = "Detail example" static let description = "Detail example detail" static var parameters: [Parameter.Type] { @@ -27,9 +29,11 @@ extension Example.Model { Parameters.CustomRequestHeader.self ] } - static let responses: [OperationResponse] = [ - .init(200, Responses.Detail.self) - ] + static var responses: [OperationResponse] { + [ + .init(200, Responses.Detail.self) + ] + } } enum Create: Operation { diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+PathItems.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+PathItems.swift index 7f31e36..6c8364e 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+PathItems.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+PathItems.swift @@ -9,26 +9,30 @@ import FeatherOpenAPI extension Example.Model { - static let pathItems: [PathItem.Type] = [ - PathItems.Main.self, - PathItems.Identified.self, - ] + static var pathItems: [PathItem.Type] { + [ + PathItems.Main.self, + PathItems.Identified.self, + ] + } enum PathItems { enum Main: PathItem { - static let path: Path = "/example/models" + static var path: Path { "/example/models" } - static let post: Operation.Type? = Operations.Create.self + static var post: Operation.Type? { Operations.Create.self } } enum Identified: PathItem { - static let path: Path = Main.path / Parameters.Id.path - static let parameters: [Parameter.Type] = [ - Parameters.Id.self - ] - - static let get: Operation.Type? = Operations.Get.self + static var path: Path { Main.path / Parameters.Id.path } + static var parameters: [Parameter.Type] { + [ + Parameters.Id.self + ] + } + + static var get: Operation.Type? { Operations.Get.self } } } diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift index 0b566cd..d48aaa0 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift @@ -9,15 +9,17 @@ import FeatherOpenAPI extension Example.Model { - static let requestBodies: [RequestBody.Type] = [ - RequestBodies.Create.self - ] + static var requestBodies: [RequestBody.Type] { + [ + RequestBodies.Create.self + ] + } enum RequestBodies { enum Create: JSONBody { static let description = "Create example" - static let schema: Schema.Type = Schemas.Create.self + static var schema: Schema.Type { Schemas.Create.self } } } } diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Responses.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Responses.swift index 5cc4990..6368d0c 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Responses.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Responses.swift @@ -10,25 +10,31 @@ import OpenAPIKit extension Example.Model { - static let responses: [Response.Type] = [ - Responses.Detail.self - ] + static var responses: [Response.Type] { + [ + Responses.Detail.self + ] + } enum Responses { enum Custom: Response { static let description = "Example" - static var contents: [OpenAPI.ContentType: Schema.Type] = [ - .xml: Schemas.Detail.self - ] + static var contents: [OpenAPI.ContentType: Schema.Type] { + [ + .xml: Schemas.Detail.self + ] + } } enum Detail: JSONResponse { static let description = "Example" - static var headers: [Header.Type] = [ - Headers.CustomResponseHeader.self - ] - static let schema: Schema.Type = Schemas.Detail.self + static var headers: [Header.Type] { + [ + Headers.CustomResponseHeader.self + ] + } + static var schema: Schema.Type { Schemas.Detail.self } } } } diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift index b2a73e2..a5a8810 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift @@ -18,9 +18,11 @@ extension Operation { extension Example.Model { - static let securitySchemes: [SecurityScheme.Type] = [ - SecuritySchemes.BearerToken.self - ] + static var securitySchemes: [SecurityScheme.Type] { + [ + SecuritySchemes.BearerToken.self + ] + } enum SecuritySchemes { diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Tags.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Tags.swift index 34fdd52..b5a3ef1 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Tags.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Tags.swift @@ -9,9 +9,11 @@ import FeatherOpenAPI extension Example.Model { - static let tags: [Tag.Type] = [ - Tags.Main.self - ] + static var tags: [Tag.Type] { + [ + Tags.Main.self + ] + } enum Tags { diff --git a/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift index 9ac6457..2c37e58 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift @@ -9,11 +9,13 @@ import FeatherOpenAPI extension ExampleDuplicatedItem.Model { - static let schemas: [Schema.Type] = [ - Schemas.Id.self, - Schemas.Key.self, - Schemas.KeySecond.self, - ] + static var schemas: [Schema.Type] { + [ + Schemas.Id.self, + Schemas.Key.self, + Schemas.KeySecond.self, + ] + } enum Schemas { diff --git a/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift index 017db73..04ab2c2 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift @@ -9,10 +9,12 @@ import FeatherOpenAPI extension ExampleMissingParentItem.Model { - static let schemas: [Schema.Type] = [ - Schemas.Id.self, - Schemas.Key.self, - ] + static var schemas: [Schema.Type] { + [ + Schemas.Id.self, + Schemas.Key.self, + ] + } enum Schemas { diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift index 0b8aa61..f17e910 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift @@ -7,6 +7,7 @@ import FeatherOpenAPI import OpenAPIKit +import Foundation struct ExampleDocument: Document { diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift index e2bc4dd..64a7137 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift @@ -7,6 +7,7 @@ import FeatherOpenAPI import OpenAPIKit +import Foundation struct ExampleDuplicatedItemDocument: Document { diff --git a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift index 236da83..863873f 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift @@ -7,6 +7,7 @@ import FeatherOpenAPI import OpenAPIKit +import Foundation struct ExampleMissingParentItemItemDocument: Document { diff --git a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift index 79369a5..47a7609 100644 --- a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift +++ b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift @@ -9,17 +9,19 @@ import Foundation import OpenAPIKit import OpenAPIKitCore import Yams -import XCTest +import Testing @testable import FeatherOpenAPI -final class FeatherOpenAPIKitTests: XCTestCase { +@Suite +struct FeatherOpenAPIKitTests { - func testRender() throws { + @Test + func render() throws { let document = ExampleDocument() - XCTAssert( + #expect( try document.schemas() .contains { $0.key.rawValue == "ExampleModelPatch" @@ -33,22 +35,25 @@ final class FeatherOpenAPIKitTests: XCTestCase { _ = try openAPIDocument.locallyDereferenced() } catch { - return XCTFail("\(error)") + Issue.record("\(error)") + return } _ = try encoder.encode(openAPIDocument) } - func testSchemaDescription() throws { + @Test + func schemaDescription() throws { struct IDSchema: NanoIDSchema {} struct Foo: NanoIDSchema {} - XCTAssertEqual(IDSchema.description, "ID description") - XCTAssertEqual(Foo.description, "Foo description") + #expect(IDSchema.description == "ID description") + #expect(Foo.description == "Foo description") } - func testDuplicatedItem() throws { + @Test + func duplicatedItem() throws { let document = ExampleDuplicatedItemDocument() var errorMessage: String = "none" @@ -60,13 +65,14 @@ final class FeatherOpenAPIKitTests: XCTestCase { errorMessage = error.message } - XCTAssertEqual( - errorMessage, - "Feather OpenAPI item id is duplicated: 'ExampleDuplicatedItemModelKey' (Did you forget to include override=true?)" + #expect( + errorMessage + == "Feather OpenAPI item id is duplicated: 'ExampleDuplicatedItemModelKey' (Did you forget to include override=true?)" ) } - func testMissingParentItem() throws { + @Test + func missingParentItem() throws { let document = ExampleMissingParentItemItemDocument() var errorMessage: String = "none" @@ -78,9 +84,9 @@ final class FeatherOpenAPIKitTests: XCTestCase { errorMessage = error.message } - XCTAssertEqual( - errorMessage, - "Feather OpenAPI item 'ExampleMissingParentItemModelKey' is set as override but has no parent. (Are the component orders correct? Or are the IDs the same?)" + #expect( + errorMessage + == "Feather OpenAPI item 'ExampleMissingParentItemModelKey' is set as override but has no parent. (Are the component orders correct? Or are the IDs the same?)" ) } diff --git a/Tests/FeatherOpenAPITests/PathComponentTests.swift b/Tests/FeatherOpenAPITests/PathComponentTests.swift index 73175e2..5d0ea30 100644 --- a/Tests/FeatherOpenAPITests/PathComponentTests.swift +++ b/Tests/FeatherOpenAPITests/PathComponentTests.swift @@ -6,7 +6,7 @@ // import Foundation -import XCTest +import Testing @testable import FeatherOpenAPI @@ -33,75 +33,88 @@ fileprivate extension Path { } } -final class PathComponentTests: XCTestCase { +@Suite +struct PathComponentTests { - func testDoesNotCompile() { + @Test + func doesNotCompile() { // let test = "foo" / "part_second" / "part_third" } - func testStringLiteralInit() { + @Test + func stringLiteralInit() { let test: Path = .init("foo") - XCTAssertEqual(test.value, "foo") + #expect(test.value == "foo") } - func testExplicitPathComponent() { + @Test + func explicitPathComponent() { let test = Path("foo") / Path("part_second") / Path("part_third") - XCTAssertEqual(test.value, "foo/part_second/part_third") + #expect(test.value == "foo/part_second/part_third") } - func testExplicitPathComponentOp() { + @Test + func explicitPathComponentOp() { let test = Path("foo") / Path("part_second") / Path("part_third") - XCTAssertEqual(test.value, "foo/part_second/part_third") + #expect(test.value == "foo/part_second/part_third") } - func testPureStringLiteralOp() { + @Test + func pureStringLiteralOp() { let test: Path = .init("foo") / "part_second" - XCTAssertEqual(test.value, "foo/part_second") + #expect(test.value == "foo/part_second") } - func testDecorInit() { + @Test + func decorInit() { let test: Path = .parameter("param") - XCTAssertEqual(test.value, "{param}") + #expect(test.value == "{param}") } - func testDecorOp() { + @Test + func decorOp() { let test: Path = .init("foo") / "part_second" / .parameter("param") / .star("star") - XCTAssertEqual(test.value, "foo/part_second/{param}/*star*") + #expect(test.value == "foo/part_second/{param}/*star*") } - func testStringInit() { + @Test + func stringInit() { let strTest = "string" let test: Path = .init(strTest) - XCTAssertEqual(test.value, "string") + #expect(test.value == "string") } - func testStringOp_1() { + @Test + func stringOp_1() { let strTest = "string" let test: Path = Path("foo") / strTest - XCTAssertEqual(test.value, "foo/string") + #expect(test.value == "foo/string") } - func testStringOp_2() { + @Test + func stringOp_2() { let strTest = "string" let test: Path = strTest / Path("foo") - XCTAssertEqual(test.value, "string/foo") + #expect(test.value == "string/foo") } - func testParameterOp() { + @Test + func parameterOp() { let paramDummy = ParameterDummy(name: "param") let test: Path = .init("foo") / "part_second" / .parameter(paramDummy) // also works with explicit name: //let test: PathComponent = .init("foo") / "part_second" / .parameter(paramDummy.name) - XCTAssertEqual(test.value, "foo/part_second/{param}") + #expect(test.value == "foo/part_second/{param}") } - func testUltimate() { + @Test + func ultimate() { // utimate op test //TODO: may replace by freestanding macros like that: // #let_path_comp("example") @@ -112,6 +125,6 @@ final class PathComponentTests: XCTestCase { let test = example / model / .superstar() / .parameter(paramVariable) - XCTAssertEqual(test.value, "example/model/********/{variable-id}") + #expect(test.value == "example/model/********/{variable-id}") } } From 7c40c57a0a81d91a6cb5506341c6001c6c192f9a Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Tue, 20 Jan 2026 20:24:18 +0100 Subject: [PATCH 06/34] db remove --- Makefile | 2 +- Package.swift | 2 +- README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index a3e1b32..8c5a82c 100644 --- a/Makefile +++ b/Makefile @@ -38,4 +38,4 @@ test: swift test --parallel docker-test: - docker build -t feather-database-tests . -f ./docker/tests/Dockerfile && docker run --rm feather-database-tests + docker build -t feather-openapi-tests . -f ./docker/tests/Dockerfile && docker run --rm feather-openapi-tests diff --git a/Package.swift b/Package.swift index bfd564e..b955fbc 100644 --- a/Package.swift +++ b/Package.swift @@ -13,7 +13,7 @@ var defaultSwiftSettings: [SwiftSetting] = // https://forums.swift.org/t/experimental-support-for-lifetime-dependencies-in-swift-6-2-and-beyond/78638 .enableExperimentalFeature("Lifetimes"), // https://github.com/swiftlang/swift/pull/65218 - .enableExperimentalFeature("AvailabilityMacro=featherDatabase 1.0:macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, visionOS 2.0"), + .enableExperimentalFeature("AvailabilityMacro=featherOpenAPI 1.0:macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, visionOS 2.0"), ] #if compiler(>=6.2) diff --git a/README.md b/README.md index 8312927..eee36dc 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Then add `FeatherOpenAPI` to your target dependencies: ![DocC API documentation](https://img.shields.io/badge/DocC-API_documentation-F05138) -API documentation is available at the following link. Refer to the mock objects in the Tests directory if you want to build a custom database driver implementation. +API documentation is available at the following link. > [!WARNING] > This repository is a work in progress, things can break until it reaches v1.0.0. From f6bcea85a0adcc96dd24add5250229ab9748c713 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Wed, 21 Jan 2026 18:59:25 +0100 Subject: [PATCH 07/34] experimental new lib --- Package.swift | 19 ++ Sources/FeatherOpenAPI30/Callback.swift | 38 ++++ Sources/FeatherOpenAPI30/Components.swift | 160 ++++++++++++++ Sources/FeatherOpenAPI30/Contact.swift | 30 +++ Sources/FeatherOpenAPI30/Content.swift | 39 ++++ Sources/FeatherOpenAPI30/Document.swift | 55 +++++ Sources/FeatherOpenAPI30/Example.swift | 53 +++++ .../ExternalDocumentation.swift | 43 ++++ Sources/FeatherOpenAPI30/Header.swift | 56 +++++ Sources/FeatherOpenAPI30/Info.swift | 53 +++++ Sources/FeatherOpenAPI30/License.swift | 26 +++ Sources/FeatherOpenAPI30/Links.swift | 38 ++++ Sources/FeatherOpenAPI30/Location.swift | 31 +++ Sources/FeatherOpenAPI30/Operation.swift | 103 +++++++++ Sources/FeatherOpenAPI30/Parameter.swift | 60 ++++++ Sources/FeatherOpenAPI30/PathItem.swift | 74 +++++++ Sources/FeatherOpenAPI30/RequestBody.swift | 53 +++++ Sources/FeatherOpenAPI30/Response.swift | 57 +++++ Sources/FeatherOpenAPI30/Schema.swift | 97 +++++++++ .../SecurityRequirement.swift | 34 +++ Sources/FeatherOpenAPI30/SecurityScheme.swift | 38 ++++ Sources/FeatherOpenAPI30/Server.swift | 31 +++ Sources/FeatherOpenAPI30/Tag.swift | 8 + Sources/FeatherOpenAPI30/Variable.swift | 28 +++ .../Example/ExampleDocument.swift | 8 + .../Example/ExampleHeaders.swift | 26 +++ .../Example/ExampleOperations.swift | 24 +++ .../Example/ExampleParameters.swift | 24 +++ .../Example/ExamplePathItems.swift | 8 + .../Example/ExampleRequestBodies.swift | 12 ++ .../Example/ExampleResponses.swift | 12 ++ .../Example/ExampleSchemas.swift | 25 +++ .../Example/ExampleSecuritySchemes.swift | 8 + .../Example/ExampleTags.swift | 8 + .../FeatherOpenAPIKitTests.swift | 201 ++++++++++++++++++ .../FeatherOpenAPIKitTests.swift | 4 +- 36 files changed, 1583 insertions(+), 1 deletion(-) create mode 100644 Sources/FeatherOpenAPI30/Callback.swift create mode 100644 Sources/FeatherOpenAPI30/Components.swift create mode 100644 Sources/FeatherOpenAPI30/Contact.swift create mode 100644 Sources/FeatherOpenAPI30/Content.swift create mode 100644 Sources/FeatherOpenAPI30/Document.swift create mode 100644 Sources/FeatherOpenAPI30/Example.swift create mode 100644 Sources/FeatherOpenAPI30/ExternalDocumentation.swift create mode 100644 Sources/FeatherOpenAPI30/Header.swift create mode 100644 Sources/FeatherOpenAPI30/Info.swift create mode 100644 Sources/FeatherOpenAPI30/License.swift create mode 100644 Sources/FeatherOpenAPI30/Links.swift create mode 100644 Sources/FeatherOpenAPI30/Location.swift create mode 100644 Sources/FeatherOpenAPI30/Operation.swift create mode 100644 Sources/FeatherOpenAPI30/Parameter.swift create mode 100644 Sources/FeatherOpenAPI30/PathItem.swift create mode 100644 Sources/FeatherOpenAPI30/RequestBody.swift create mode 100644 Sources/FeatherOpenAPI30/Response.swift create mode 100644 Sources/FeatherOpenAPI30/Schema.swift create mode 100644 Sources/FeatherOpenAPI30/SecurityRequirement.swift create mode 100644 Sources/FeatherOpenAPI30/SecurityScheme.swift create mode 100644 Sources/FeatherOpenAPI30/Server.swift create mode 100644 Sources/FeatherOpenAPI30/Tag.swift create mode 100644 Sources/FeatherOpenAPI30/Variable.swift create mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExampleDocument.swift create mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExampleHeaders.swift create mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExampleOperations.swift create mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExampleParameters.swift create mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExamplePathItems.swift create mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExampleRequestBodies.swift create mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExampleResponses.swift create mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExampleSchemas.swift create mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExampleSecuritySchemes.swift create mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExampleTags.swift create mode 100644 Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift diff --git a/Package.swift b/Package.swift index b955fbc..034dfb2 100644 --- a/Package.swift +++ b/Package.swift @@ -64,12 +64,23 @@ let package = Package( name: "FeatherOpenAPI", dependencies: [ .product(name: "OpenAPIKit", package: "OpenAPIKit"), + .product(name: "OpenAPIKit30", package: "OpenAPIKit"), + .product(name: "OpenAPIKitCompat", package: "OpenAPIKit"), + .product(name: "Yams", package: "yams"), ], swiftSettings: defaultSwiftSettings, plugins: [ .plugin(name: "FeatherOpenAPIGenerator") ], ), + .target( + name: "FeatherOpenAPI30", + dependencies: [ + .product(name: "OpenAPIKit30", package: "OpenAPIKit"), + .product(name: "OpenAPIKitCompat", package: "OpenAPIKit"), + ], + swiftSettings: defaultSwiftSettings + ), .executableTarget( name: "feather-openapi-generator", dependencies: [ @@ -85,5 +96,13 @@ let package = Package( ], swiftSettings: defaultSwiftSettings, ), + .testTarget( + name: "FeatherOpenAPI30Tests", + dependencies: [ + .product(name: "Yams", package: "Yams"), + .target(name: "FeatherOpenAPI30"), + ], + swiftSettings: defaultSwiftSettings, + ), ] ) diff --git a/Sources/FeatherOpenAPI30/Callback.swift b/Sources/FeatherOpenAPI30/Callback.swift new file mode 100644 index 0000000..92c7ada --- /dev/null +++ b/Sources/FeatherOpenAPI30/Callback.swift @@ -0,0 +1,38 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public struct CallbackID: Hashable { + + public var rawValue: String + + public init( + _ rawValue: String + ) { + self.rawValue = rawValue + } +} + +public protocol CallbackRepresentable { + func openAPICallback() -> Callback +} + +public struct Callback: CallbackRepresentable { + + public func openAPICallback() -> Callback { + fatalError() + // .init( + // operationId: <#T##String#>, + // parameters: <#T##OrderedDictionary>#>, + // requestBody: <#T##Either?#>, + // description: <#T##String?#>, + // server: <#T##OpenAPI.Server?#>, + // vendorExtensions: <#T##[String : AnyCodable]#> + // ) + } +} diff --git a/Sources/FeatherOpenAPI30/Components.swift b/Sources/FeatherOpenAPI30/Components.swift new file mode 100644 index 0000000..82ac42e --- /dev/null +++ b/Sources/FeatherOpenAPI30/Components.swift @@ -0,0 +1,160 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol ComponentsRepresentable { + func openAPIComponents() -> OpenAPI.Components +} + +public struct Components: ComponentsRepresentable { + + public var schemas: OrderedDictionary + public var parameters: + OrderedDictionary + public var examples: OrderedDictionary + public var responses: OrderedDictionary + public var requestBodies: + OrderedDictionary + public var headers: OrderedDictionary + public var securitySchemes: + OrderedDictionary + public var links: OrderedDictionary + // public var callbacks: OrderedDictionary + public var vendorExtensions: [String: AnyCodable] + + public init( + schemas: OrderedDictionary = [:], + parameters: OrderedDictionary = + [:], + examples: OrderedDictionary = [:], + responses: OrderedDictionary = [:], + requestBodies: OrderedDictionary< + RequestBodyID, RequestBodyRepresentable + > = [:], + headers: OrderedDictionary = [:], + securitySchemes: OrderedDictionary< + SecuritySchemeID, SecuritySchemeRepresentable + > = [:], + links: OrderedDictionary = [:], + // callbacks: OrderedDictionary = [:], + vendorExtensions: [String: AnyCodable] = [:] + ) { + self.schemas = schemas + self.parameters = parameters + self.examples = examples + self.responses = responses + self.requestBodies = requestBodies + self.headers = headers + self.securitySchemes = securitySchemes + self.links = links + // self.callbacks = callbacks + self.vendorExtensions = vendorExtensions + } + + func openAPISchemas() -> OpenAPI.ComponentDictionary { + var result: OpenAPI.ComponentDictionary = [:] + + for (key, value) in schemas { + result[.init(stringLiteral: key.rawValue)] = value.openAPISchema() + } + return result + } + + func openAPIResponses() -> OpenAPI.ComponentDictionary { + var result: OpenAPI.ComponentDictionary = [:] + + for (key, value) in responses { + result[.init(stringLiteral: key.rawValue)] = value.openAPIResponse() + } + return result + } + + func openAPIParameters() -> OpenAPI.ComponentDictionary { + var result: OpenAPI.ComponentDictionary = [:] + + for (key, value) in parameters { + result[.init(stringLiteral: key.rawValue)] = + value.openAPIParameter() + } + return result + } + + func openAPIExamples() -> OpenAPI.ComponentDictionary { + var result: OpenAPI.ComponentDictionary = [:] + + for (key, value) in examples { + result[.init(stringLiteral: key.rawValue)] = value.openAPIExample() + } + return result + } + + func openAPIRequestBodies() -> OpenAPI.ComponentDictionary + { + var result: OpenAPI.ComponentDictionary = [:] + + for (key, value) in requestBodies { + result[.init(stringLiteral: key.rawValue)] = + value.openAPIRequestBody() + } + return result + } + + func openAPIHeaders() -> OpenAPI.ComponentDictionary { + var result: OpenAPI.ComponentDictionary = [:] + + for (key, value) in headers { + result[.init(stringLiteral: key.rawValue)] = value.openAPIHeader() + } + return result + } + + func openAPISecuritySchemes() + -> OpenAPI.ComponentDictionary + { + var result: OpenAPI.ComponentDictionary = [:] + + for (key, value) in securitySchemes { + result[.init(stringLiteral: key.rawValue)] = + value.openAPISecurityScheme() + } + return result + } + + func openAPILinks() -> OpenAPI.ComponentDictionary { + var result: OpenAPI.ComponentDictionary = [:] + + for (key, value) in links { + result[.init(stringLiteral: key.rawValue)] = value.openAPILink() + } + return result + } + + // func openAPICallbacks() -> OpenAPI.ComponentDictionary { + // var result: OpenAPI.ComponentDictionary = [:] + // + // for (key, value) in callbacks { + // result[.init(stringLiteral: key.rawValue)] = value.openAPICallback() + // } + // return result + // } + + public func openAPIComponents() -> OpenAPI.Components { + .init( + schemas: openAPISchemas(), + responses: openAPIResponses(), + parameters: openAPIParameters(), + examples: openAPIExamples(), + requestBodies: openAPIRequestBodies(), + headers: openAPIHeaders(), + securitySchemes: openAPISecuritySchemes(), + links: openAPILinks(), + callbacks: .init(), + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI30/Contact.swift b/Sources/FeatherOpenAPI30/Contact.swift new file mode 100644 index 0000000..4dc904f --- /dev/null +++ b/Sources/FeatherOpenAPI30/Contact.swift @@ -0,0 +1,30 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol ContactRepresentable { + + func openAPIContact() -> OpenAPI.Document.Info.Contact +} + +public struct Contact: ContactRepresentable { + + public let name: String? + public let url: URLRepresentable? + public let email: String? + public var vendorExtensions: [String: AnyCodable] + + public func openAPIContact() -> OpenAPI.Document.Info.Contact { + .init( + name: name, + url: url?.url(), + email: email, + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI30/Content.swift b/Sources/FeatherOpenAPI30/Content.swift new file mode 100644 index 0000000..32252d5 --- /dev/null +++ b/Sources/FeatherOpenAPI30/Content.swift @@ -0,0 +1,39 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol ContentRepresentable { + func openAPIContent() -> OpenAPI.Content +} + +public struct Content: ContentRepresentable { + + public var schema: SchemaID + public var example: AnyCodable? + + public init( + schema: SchemaID, + example: AnyCodable? = nil + ) { + self.schema = schema + self.example = example + } + + public func openAPIContent() -> OpenAPI.Content { + .init( + schemaReference: .component(named: schema.rawValue), + example: example, + encoding: [:], + vendorExtensions: [:] + ) + } +} + +// +// +//public typealias Map = OrderedDictionary diff --git a/Sources/FeatherOpenAPI30/Document.swift b/Sources/FeatherOpenAPI30/Document.swift new file mode 100644 index 0000000..d8754b0 --- /dev/null +++ b/Sources/FeatherOpenAPI30/Document.swift @@ -0,0 +1,55 @@ +// +// File 2.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol DocumentRepresentable { + func openAPIDocument() -> OpenAPI.Document +} + +public struct Document: DocumentRepresentable { + + public var info: InfoRepresentable + public var servers: [ServerRepresentable] + public var paths: OrderedDictionary + public var components: ComponentsRepresentable + public var security: [SecurityRequirementRepresentable] + public var externalDocumentation: ExternalDocumentationRepresentable? + public var vendorExtensions: [String: AnyCodable] + + public init( + info: InfoRepresentable, + servers: [ServerRepresentable] = [], + paths: OrderedDictionary, + components: ComponentsRepresentable, + security: [SecurityRequirementRepresentable] = [], + externalDocumentation: ExternalDocumentationRepresentable? = nil, + vendorExtensions: [String: AnyCodable] = [:] + ) { + self.info = info + self.servers = servers + self.paths = paths + self.components = components + self.security = security + self.externalDocumentation = externalDocumentation + self.vendorExtensions = vendorExtensions + } + + public func openAPIDocument() -> OpenAPI.Document { + .init( + openAPIVersion: .v3_0_0, + info: info.openAPIInfo(), + servers: servers.map { $0.openAPIServer() }, + paths: self.paths.mapValues { .init($0.openAPIPathItem()) }, + components: components.openAPIComponents(), + security: security.map { $0.openAPISecurityRequirement() }, + tags: nil, + externalDocs: externalDocumentation?.openAPIExternalDocumentation(), + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI30/Example.swift b/Sources/FeatherOpenAPI30/Example.swift new file mode 100644 index 0000000..4fb78cb --- /dev/null +++ b/Sources/FeatherOpenAPI30/Example.swift @@ -0,0 +1,53 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public struct ExampleID: Hashable { + + public var rawValue: String + + public init( + _ rawValue: String + ) { + self.rawValue = rawValue + } +} + +public protocol ExampleRepresentable { + func openAPIExample() -> OpenAPI.Example +} + +public struct Example: ExampleRepresentable { + + public var summary: String? + public var description: String? + public var value: AnyCodable + public var vendorExtensions: [String: AnyCodable] + + public init( + summary: String? = nil, + description: String? = nil, + value: AnyCodable, + vendorExtensions: [String: AnyCodable] = [:] + ) { + self.summary = summary + self.description = description + self.value = value + self.vendorExtensions = vendorExtensions + } + + public func openAPIExample() -> OpenAPI.Example { + .init( + summary: summary, + description: description, + value: .init(value), + vendorExtensions: vendorExtensions + ) + } + +} diff --git a/Sources/FeatherOpenAPI30/ExternalDocumentation.swift b/Sources/FeatherOpenAPI30/ExternalDocumentation.swift new file mode 100644 index 0000000..a7fa9f9 --- /dev/null +++ b/Sources/FeatherOpenAPI30/ExternalDocumentation.swift @@ -0,0 +1,43 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +import OpenAPIKit30 + +public protocol ExternalDocumentationRepresentable { + func openAPIExternalDocumentation() -> OpenAPI.ExternalDocumentation +} + +public struct ExternalDocumentation: ExternalDocumentationRepresentable { + public var url: String + public var description: String? + public var vendorExtensions: [String: AnyCodable] + + public init( + url: String, + description: String? = nil, + vendorExtensions: [String: AnyCodable] = [:] + ) { + self.url = url + self.description = description + self.vendorExtensions = vendorExtensions + } + + public func openAPIExternalDocumentation() -> OpenAPI.ExternalDocumentation + { + .init( + description: description, + url: .init(string: url)!, + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI30/Header.swift b/Sources/FeatherOpenAPI30/Header.swift new file mode 100644 index 0000000..5e22c83 --- /dev/null +++ b/Sources/FeatherOpenAPI30/Header.swift @@ -0,0 +1,56 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public struct HeaderID: Hashable { + + public var rawValue: String + + public init( + _ rawValue: String + ) { + self.rawValue = rawValue + } +} + +public protocol HeaderRepresentable { + func openAPIHeader() -> OpenAPI.Header +} + +public struct Header: HeaderRepresentable { + + public var schema: SchemaID + public var description: String? + public var required: Bool + public var deprecated: Bool + public var vendorExtensions: [String: AnyCodable] + + public init( + schema: SchemaID, + description: String? = nil, + required: Bool = false, + deprecated: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) { + self.schema = schema + self.description = description + self.required = required + self.deprecated = deprecated + self.vendorExtensions = vendorExtensions + } + + public func openAPIHeader() -> OpenAPI.Header { + .init( + schemaReference: .component(named: schema.rawValue), + description: description, + required: required, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI30/Info.swift b/Sources/FeatherOpenAPI30/Info.swift new file mode 100644 index 0000000..a532e5e --- /dev/null +++ b/Sources/FeatherOpenAPI30/Info.swift @@ -0,0 +1,53 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol InfoRepresentable { + + func openAPIInfo() -> OpenAPI.Document.Info +} + +public struct Info: InfoRepresentable { + public var title: String + public var description: String? + public var termsOfService: URLRepresentable? + public var contact: ContactRepresentable? + public var license: LicenseRepresentable? + public var version: String + public var vendorExtensions: [String: AnyCodable] + + public init( + title: String, + description: String? = nil, + termsOfService: URLRepresentable? = nil, + contact: ContactRepresentable? = nil, + license: LicenseRepresentable? = nil, + version: String, + vendorExtensions: [String: AnyCodable] = [:] + ) { + self.title = title + self.description = description + self.termsOfService = termsOfService + self.contact = contact + self.license = license + self.version = version + self.vendorExtensions = vendorExtensions + } + + public func openAPIInfo() -> OpenAPI.Document.Info { + .init( + title: title, + description: description, + termsOfService: termsOfService?.url(), + contact: contact.map { $0.openAPIContact() }, + license: license.map { $0.openAPILicense() }, + version: version, + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI30/License.swift b/Sources/FeatherOpenAPI30/License.swift new file mode 100644 index 0000000..728cffd --- /dev/null +++ b/Sources/FeatherOpenAPI30/License.swift @@ -0,0 +1,26 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol LicenseRepresentable { + func openAPILicense() -> OpenAPI.Document.Info.License +} + +public struct License: LicenseRepresentable { + public let name: String + public let url: URLRepresentable? + public var vendorExtensions: [String: AnyCodable] + + public func openAPILicense() -> OpenAPI.Document.Info.License { + .init( + name: name, + url: url?.url(), + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI30/Links.swift b/Sources/FeatherOpenAPI30/Links.swift new file mode 100644 index 0000000..64a7500 --- /dev/null +++ b/Sources/FeatherOpenAPI30/Links.swift @@ -0,0 +1,38 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public struct LinkID: Hashable { + + public var rawValue: String + + public init( + _ rawValue: String + ) { + self.rawValue = rawValue + } +} + +public protocol LinkRepresentable { + func openAPILink() -> OpenAPI.Link +} + +public struct Link: LinkRepresentable { + + public func openAPILink() -> OpenAPI.Link { + fatalError() + // .init( + // operationId: <#T##String#>, + // parameters: <#T##OrderedDictionary>#>, + // requestBody: <#T##Either?#>, + // description: <#T##String?#>, + // server: <#T##OpenAPI.Server?#>, + // vendorExtensions: <#T##[String : AnyCodable]#> + // ) + } +} diff --git a/Sources/FeatherOpenAPI30/Location.swift b/Sources/FeatherOpenAPI30/Location.swift new file mode 100644 index 0000000..20d1e82 --- /dev/null +++ b/Sources/FeatherOpenAPI30/Location.swift @@ -0,0 +1,31 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +public protocol URLRepresentable { + func url() -> URL +} + +public struct Location: URLRepresentable { + + public var string: String + + public init( + _ string: String + ) { + self.string = string + } + + public func url() -> URL { + .init(string: string)! + } +} diff --git a/Sources/FeatherOpenAPI30/Operation.swift b/Sources/FeatherOpenAPI30/Operation.swift new file mode 100644 index 0000000..42d708d --- /dev/null +++ b/Sources/FeatherOpenAPI30/Operation.swift @@ -0,0 +1,103 @@ +// +// File 2.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol OperationRepresentable { + func openAPIOperation() -> OpenAPI.Operation +} + +public struct Operation: OperationRepresentable { + + public var tags: [String]? + public var summary: String? + public var description: String? + public var externalDocs: ExternalDocumentationRepresentable? + public var operationId: String? + + public var parameters: [ParameterID] + public var requestBody: RequestBodyID? + public var responses: + OrderedDictionary + + public var deprecated: Bool + public var security: [SecurityRequirementRepresentable]? + public var servers: [ServerRepresentable]? + public var vendorExtensions: [String: AnyCodable] + + public init( + id: String? = nil, + tags: [String]? = nil, + summary: String? = nil, + description: String? = nil, + externalDocs: ExternalDocumentationRepresentable? = nil, + parameters: [ParameterID] = [], + requestBody: RequestBodyID? = nil, + responses: OrderedDictionary, + deprecated: Bool = false, + security: [SecurityRequirementRepresentable]? = nil, + servers: [ServerRepresentable]? = nil, + vendorExtensions: [String: AnyCodable] = [:] + ) { + self.tags = tags + self.summary = summary + self.description = description + self.externalDocs = externalDocs + self.operationId = id + self.parameters = parameters + self.requestBody = requestBody + self.responses = responses + self.deprecated = deprecated + self.security = security + self.servers = servers + self.vendorExtensions = vendorExtensions + } + + public func openAPIOperation() -> OpenAPI.Operation { + if let requestBody { + return .init( + tags: tags, + summary: summary, + description: description, + externalDocs: externalDocs?.openAPIExternalDocumentation(), + operationId: operationId, + parameters: parameters.map { + .reference(.component(named: $0.rawValue)) + }, + requestBody: .reference( + .component( + named: requestBody.rawValue + ) + ), + responses: responses.mapValues { + .reference(.component(named: $0.rawValue)) + }, + callbacks: .init(), + deprecated: deprecated, + security: security?.map { $0.openAPISecurityRequirement() }, + servers: servers?.map { $0.openAPIServer() }, + vendorExtensions: vendorExtensions + ) + } + return .init( + tags: tags, + summary: summary, + description: description, + externalDocs: externalDocs?.openAPIExternalDocumentation(), + operationId: operationId, + parameters: parameters.map { + .reference(.component(named: $0.rawValue)) + }, + responses: .init(), + callbacks: .init(), + deprecated: deprecated, + security: security?.map { $0.openAPISecurityRequirement() }, + servers: servers?.map { $0.openAPIServer() }, + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI30/Parameter.swift b/Sources/FeatherOpenAPI30/Parameter.swift new file mode 100644 index 0000000..270c35f --- /dev/null +++ b/Sources/FeatherOpenAPI30/Parameter.swift @@ -0,0 +1,60 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public struct ParameterID: Hashable { + + public var rawValue: String + + public init( + _ rawValue: String + ) { + self.rawValue = rawValue + } +} + +public protocol ParameterRepresentable { + func openAPIParameter() -> OpenAPI.Parameter +} + +public struct Parameter: ParameterRepresentable { + + public var name: String + public var context: OpenAPI.Parameter.Context + public var schema: SchemaID + public var description: String? + public var deprecated: Bool + public var vendorExtensions: [String: AnyCodable] + + public init( + name: String, + context: OpenAPI.Parameter.Context, + schema: SchemaID, + description: String? = nil, + deprecated: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) { + self.name = name + self.context = context + self.schema = schema + self.description = description + self.deprecated = deprecated + self.vendorExtensions = vendorExtensions + } + + public func openAPIParameter() -> OpenAPI.Parameter { + .init( + name: name, + context: context, + schemaReference: .component(named: schema.rawValue), + description: description, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI30/PathItem.swift b/Sources/FeatherOpenAPI30/PathItem.swift new file mode 100644 index 0000000..34d1783 --- /dev/null +++ b/Sources/FeatherOpenAPI30/PathItem.swift @@ -0,0 +1,74 @@ +// +// File 2.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol PathItemRepresentable { + func openAPIPathItem() -> OpenAPI.PathItem +} + +public struct PathItem: PathItemRepresentable { + + public var summary: String? + public var description: String? + public var servers: [ServerRepresentable]? + public var get: OperationRepresentable? + public var put: OperationRepresentable? + public var post: OperationRepresentable? + public var delete: OperationRepresentable? + public var options: OperationRepresentable? + public var head: OperationRepresentable? + public var patch: OperationRepresentable? + public var trace: OperationRepresentable? + public var vendorExtensions: [String: AnyCodable] + + public init( + summary: String? = nil, + description: String? = nil, + servers: [ServerRepresentable]? = nil, + get: OperationRepresentable? = nil, + put: OperationRepresentable? = nil, + post: OperationRepresentable? = nil, + delete: OperationRepresentable? = nil, + options: OperationRepresentable? = nil, + head: OperationRepresentable? = nil, + patch: OperationRepresentable? = nil, + trace: OperationRepresentable? = nil, + vendorExtensions: [String: AnyCodable] = [:] + ) { + self.summary = summary + self.description = description + self.servers = servers + self.get = get + self.put = put + self.post = post + self.delete = delete + self.options = options + self.head = head + self.patch = patch + self.trace = trace + self.vendorExtensions = vendorExtensions + } + + public func openAPIPathItem() -> OpenAPI.PathItem { + .init( + summary: summary, + description: description, + servers: servers?.map { $0.openAPIServer() }, + parameters: [], + get: get?.openAPIOperation(), + put: put?.openAPIOperation(), + post: post?.openAPIOperation(), + delete: delete?.openAPIOperation(), + options: options?.openAPIOperation(), + head: head?.openAPIOperation(), + patch: patch?.openAPIOperation(), + trace: trace?.openAPIOperation(), + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI30/RequestBody.swift b/Sources/FeatherOpenAPI30/RequestBody.swift new file mode 100644 index 0000000..e146519 --- /dev/null +++ b/Sources/FeatherOpenAPI30/RequestBody.swift @@ -0,0 +1,53 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public struct RequestBodyID: Hashable { + + public var rawValue: String + + public init( + _ rawValue: String + ) { + self.rawValue = rawValue + } +} + +public protocol RequestBodyRepresentable { + func openAPIRequestBody() -> OpenAPI.Request +} + +public struct RequestBody: RequestBodyRepresentable { + + public var description: String? + public var content: + OrderedDictionary + public var required: Bool + public var vendorExtensions: [String: AnyCodable] + + public init( + description: String? = nil, + content: OrderedDictionary, + required: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) { + self.description = description + self.content = content + self.required = required + self.vendorExtensions = vendorExtensions + } + + public func openAPIRequestBody() -> OpenAPI.Request { + .init( + description: description, + content: content.mapValues { $0.openAPIContent() }, + required: required, + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI30/Response.swift b/Sources/FeatherOpenAPI30/Response.swift new file mode 100644 index 0000000..d59b0a4 --- /dev/null +++ b/Sources/FeatherOpenAPI30/Response.swift @@ -0,0 +1,57 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public struct ResponseID: Hashable { + + public var rawValue: String + + public init( + _ rawValue: String + ) { + self.rawValue = rawValue + } +} + +public protocol ResponseRepresentable { + func openAPIResponse() -> OpenAPI.Response +} + +public struct Response: ResponseRepresentable { + + public var description: String + public var headers: OrderedDictionary + public var content: + OrderedDictionary + public var vendorExtensions: [String: AnyCodable] + + public init( + description: String, + headers: OrderedDictionary = [:], + content: OrderedDictionary = + [:], + vendorExtensions: [String: AnyCodable] = [:] + ) { + self.description = description + self.headers = headers + self.content = content + self.vendorExtensions = vendorExtensions + } + + public func openAPIResponse() -> OpenAPI.Response { + .init( + description: description, + headers: headers.mapValues { + .init(.component(named: $0.rawValue)) + }, + content: content.mapValues { $0.openAPIContent() }, + links: [:], + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI30/Schema.swift b/Sources/FeatherOpenAPI30/Schema.swift new file mode 100644 index 0000000..03d8e53 --- /dev/null +++ b/Sources/FeatherOpenAPI30/Schema.swift @@ -0,0 +1,97 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public struct SchemaID: Hashable { + + public var rawValue: String + + public init( + _ rawValue: String + ) { + self.rawValue = rawValue + } +} + +public protocol SchemaRepresentable { + + func openAPISchema() -> JSONSchema +} + +public struct StringSchema: SchemaRepresentable { + + public var format: JSONTypeFormat.StringFormat + public var required: Bool + public var nullable: Bool? + // public var permissions: Permissions? + public var deprecated: Bool? + public var title: String? + public var description: String? + public var discriminator: OpenAPI.Discriminator? + public var externalDocs: OpenAPI.ExternalDocumentation? + public var minLength: Int? + public var maxLength: Int? + public var pattern: String? + public var allowedValues: [AnyCodable]? + public var defaultValue: AnyCodable? + public var example: AnyCodable? + + public init( + format: JSONTypeFormat.StringFormat = .generic, + required: Bool = false, + nullable: Bool? = nil, + // permissions: Permissions? = nil, + deprecated: Bool? = nil, + title: String? = nil, + description: String? = nil, + discriminator: OpenAPI.Discriminator? = nil, + externalDocs: OpenAPI.ExternalDocumentation? = nil, + minLength: Int? = nil, + maxLength: Int? = nil, + pattern: String? = nil, + allowedValues: [AnyCodable]? = nil, + defaultValue: AnyCodable? = nil, + example: AnyCodable? = nil + ) { + self.format = format + self.required = required + self.nullable = nullable + // self.permissions = permissions + self.deprecated = deprecated + self.title = title + self.description = description + self.discriminator = discriminator + self.externalDocs = externalDocs + self.minLength = minLength + self.maxLength = maxLength + self.pattern = pattern + self.allowedValues = allowedValues + self.defaultValue = defaultValue + self.example = example + } + + public func openAPISchema() -> JSONSchema { + .string( + format: format, + required: required, + nullable: nullable, + permissions: nil, + deprecated: deprecated, + title: title, + description: description, + discriminator: discriminator, + externalDocs: externalDocs, + minLength: minLength, + maxLength: maxLength, + pattern: pattern, + allowedValues: allowedValues, + defaultValue: defaultValue, + example: example + ) + } +} diff --git a/Sources/FeatherOpenAPI30/SecurityRequirement.swift b/Sources/FeatherOpenAPI30/SecurityRequirement.swift new file mode 100644 index 0000000..9298d4c --- /dev/null +++ b/Sources/FeatherOpenAPI30/SecurityRequirement.swift @@ -0,0 +1,34 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol SecurityRequirementRepresentable { + + // [JSONReference: [String]] + func openAPISecurityRequirement() -> OpenAPI.SecurityRequirement +} + +public struct SecurityRequirement: SecurityRequirementRepresentable { + + public var requirements: [String: [String]] + + public init( + _ requirements: [String: [String]] + ) { + self.requirements = requirements + } + + //[JSONReference: [String]] + public func openAPISecurityRequirement() -> OpenAPI.SecurityRequirement { + var result: [JSONReference: [String]] = [:] + for (key, value) in self.requirements { + result[.component(named: key)] = value + } + return result + } +} diff --git a/Sources/FeatherOpenAPI30/SecurityScheme.swift b/Sources/FeatherOpenAPI30/SecurityScheme.swift new file mode 100644 index 0000000..2a9f415 --- /dev/null +++ b/Sources/FeatherOpenAPI30/SecurityScheme.swift @@ -0,0 +1,38 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public struct SecuritySchemeID: Hashable { + + public var rawValue: String + + public init( + _ rawValue: String + ) { + self.rawValue = rawValue + } +} + +public protocol SecuritySchemeRepresentable { + func openAPISecurityScheme() -> OpenAPI.SecurityScheme +} + +public struct SecurityScheme: SecuritySchemeRepresentable { + + public var type: OpenAPI.SecurityScheme.SecurityType + public var description: String? + public var vendorExtensions: [String: AnyCodable] + + public func openAPISecurityScheme() -> OpenAPI.SecurityScheme { + .init( + type: type, + description: description, + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI30/Server.swift b/Sources/FeatherOpenAPI30/Server.swift new file mode 100644 index 0000000..2c5dfa4 --- /dev/null +++ b/Sources/FeatherOpenAPI30/Server.swift @@ -0,0 +1,31 @@ +// +// File 2.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol ServerRepresentable { + func openAPIServer() -> OpenAPI.Server +} + +public struct Server: ServerRepresentable { + + public var url: URLRepresentable + public var description: String? + public var variables: OrderedDictionary + public var vendorExtensions: [String: AnyCodable] + + public func openAPIServer() -> OpenAPI.Server { + .init( + url: url.url(), + description: description, + variables: variables.mapValues { + $0.openAPIServerVariable() + }, + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI30/Tag.swift b/Sources/FeatherOpenAPI30/Tag.swift new file mode 100644 index 0000000..d1d5159 --- /dev/null +++ b/Sources/FeatherOpenAPI30/Tag.swift @@ -0,0 +1,8 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import Foundation diff --git a/Sources/FeatherOpenAPI30/Variable.swift b/Sources/FeatherOpenAPI30/Variable.swift new file mode 100644 index 0000000..7c0d594 --- /dev/null +++ b/Sources/FeatherOpenAPI30/Variable.swift @@ -0,0 +1,28 @@ +// +// File 2.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol ServerVariableRepresentable { + func openAPIServerVariable() -> OpenAPI.Server.Variable +} + +public struct ServerVariable: ServerVariableRepresentable { + public var `enum`: [String] + public var `default`: String + public var description: String? + public var vendorExtensions: [String: AnyCodable] + + public func openAPIServerVariable() -> OpenAPI.Server.Variable { + .init( + enum: self.enum, + default: self.default, + description: self.description, + vendorExtensions: self.vendorExtensions + ) + } +} diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleDocument.swift b/Tests/FeatherOpenAPI30Tests/Example/ExampleDocument.swift new file mode 100644 index 0000000..e60b094 --- /dev/null +++ b/Tests/FeatherOpenAPI30Tests/Example/ExampleDocument.swift @@ -0,0 +1,8 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import FeatherOpenAPI30 diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleHeaders.swift b/Tests/FeatherOpenAPI30Tests/Example/ExampleHeaders.swift new file mode 100644 index 0000000..b6e4607 --- /dev/null +++ b/Tests/FeatherOpenAPI30Tests/Example/ExampleHeaders.swift @@ -0,0 +1,26 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import FeatherOpenAPI30 + +extension HeaderID { + static var example: Self { .init(#function) } +} + +extension HeaderID { + static var customResponse: Self { .init(#function) } +} + +extension Header { + + static var customResponse: Self { + .init( + schema: .customResponseHeader, + description: "My custom response header", + ) + } +} diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleOperations.swift b/Tests/FeatherOpenAPI30Tests/Example/ExampleOperations.swift new file mode 100644 index 0000000..b0522af --- /dev/null +++ b/Tests/FeatherOpenAPI30Tests/Example/ExampleOperations.swift @@ -0,0 +1,24 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import FeatherOpenAPI30 +import OpenAPIKit30 + +extension Operation { + + static var foo: Self { + .init( + parameters: [ + .foo + ], + requestBody: .foo, + responses: [ + 200: .foo + ] + ) + } +} diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleParameters.swift b/Tests/FeatherOpenAPI30Tests/Example/ExampleParameters.swift new file mode 100644 index 0000000..d58924e --- /dev/null +++ b/Tests/FeatherOpenAPI30Tests/Example/ExampleParameters.swift @@ -0,0 +1,24 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 +import FeatherOpenAPI30 + +extension ParameterID { + static var foo: Self { .init(#function) } +} + +extension Parameter { + + static var foo: Self { + Parameter( + name: "foo", + context: .path, + schema: .test + ) + } +} diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExamplePathItems.swift b/Tests/FeatherOpenAPI30Tests/Example/ExamplePathItems.swift new file mode 100644 index 0000000..e60b094 --- /dev/null +++ b/Tests/FeatherOpenAPI30Tests/Example/ExamplePathItems.swift @@ -0,0 +1,8 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import FeatherOpenAPI30 diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleRequestBodies.swift b/Tests/FeatherOpenAPI30Tests/Example/ExampleRequestBodies.swift new file mode 100644 index 0000000..5653785 --- /dev/null +++ b/Tests/FeatherOpenAPI30Tests/Example/ExampleRequestBodies.swift @@ -0,0 +1,12 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import FeatherOpenAPI30 + +extension RequestBodyID { + static var foo: Self { .init(#function) } +} diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleResponses.swift b/Tests/FeatherOpenAPI30Tests/Example/ExampleResponses.swift new file mode 100644 index 0000000..3f84eed --- /dev/null +++ b/Tests/FeatherOpenAPI30Tests/Example/ExampleResponses.swift @@ -0,0 +1,12 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import FeatherOpenAPI30 + +extension ResponseID { + static var foo: Self { .init(#function) } +} diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleSchemas.swift b/Tests/FeatherOpenAPI30Tests/Example/ExampleSchemas.swift new file mode 100644 index 0000000..69bc308 --- /dev/null +++ b/Tests/FeatherOpenAPI30Tests/Example/ExampleSchemas.swift @@ -0,0 +1,25 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import FeatherOpenAPI30 +import OpenAPIKit30 + +extension SchemaID { + static var test: Self { .init(#function) } + + static var customResponseHeader: Self { .init(#function) } +} + +extension StringSchema { + + static var customResponseHeader: Self { + .init( + description: "Custom header", + example: "my-example-key" + ) + } +} diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleSecuritySchemes.swift b/Tests/FeatherOpenAPI30Tests/Example/ExampleSecuritySchemes.swift new file mode 100644 index 0000000..e60b094 --- /dev/null +++ b/Tests/FeatherOpenAPI30Tests/Example/ExampleSecuritySchemes.swift @@ -0,0 +1,8 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import FeatherOpenAPI30 diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleTags.swift b/Tests/FeatherOpenAPI30Tests/Example/ExampleTags.swift new file mode 100644 index 0000000..e60b094 --- /dev/null +++ b/Tests/FeatherOpenAPI30Tests/Example/ExampleTags.swift @@ -0,0 +1,8 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import FeatherOpenAPI30 diff --git a/Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift new file mode 100644 index 0000000..8e07070 --- /dev/null +++ b/Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift @@ -0,0 +1,201 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 20/01/2024. +// + +import Foundation +import OpenAPIKit +import OpenAPIKit30 +import OpenAPIKitCompat +import Yams +import Testing + +@testable import FeatherOpenAPI30 + +@Suite +struct FeatherOpenAPIKitTests { + + @Test + func example() throws { + + let doc = Document( + info: Info( + title: "foo", + version: "1.0.0" + ), + paths: [ + "foo": PathItem( + get: Operation( + parameters: [ + .foo + ], + requestBody: .foo, + responses: [ + 200: .foo + ] + ) + ) + ], + components: Components( + schemas: [ + .test: StringSchema(), + .customResponseHeader: StringSchema.customResponseHeader, + ], + parameters: [ + .foo: Parameter.foo + ], + responses: [ + .foo: Response( + description: "foo", + headers: [ + "X-Custom-Response-Header": .customResponse + ], + content: [ + .aac: Content(schema: .test) + ], + ) + ], + requestBodies: [ + .foo: RequestBody( + content: [ + .any: Content(schema: .test) + ] + ) + ], + headers: [ + .customResponse: Header.customResponse + ] + ), + ) + + let openAPIdoc = doc.openAPIDocument() + + let encoder = YAMLEncoder() + + do { + _ = + try openAPIdoc + .locallyDereferenced() + .resolved() + } + catch { + print(error) + throw error + } + + let result = try encoder.encode(openAPIdoc) + print(result) + + } + + func renderTest() throws { + + let paths: OpenAPIKit30.OpenAPI.PathItem.Map = [ + "foo": .init( + .init( + summary: "foo", + description: nil, + servers: nil, + parameters: [], + get: .init( + requestBody: .init( + .component( + named: "foo" + ) + ), + responses: [:] + ), + put: nil, + post: nil, + delete: nil, + options: nil, + head: nil, + patch: nil, + trace: nil, + vendorExtensions: [:] + ) + ) + ] + + let doc = OpenAPIKit30.OpenAPI.Document( + info: .init( + title: "foo", + version: "3.0.0" + ), + servers: [], + paths: paths, + components: .init( + schemas: [ + "schemaID": .string( + format: .dateTime, + required: true, + nullable: false, + permissions: nil, + deprecated: nil, + title: nil, + description: nil, + discriminator: nil, + externalDocs: nil, + minLength: nil, + maxLength: nil, + pattern: nil, + allowedValues: nil, + defaultValue: nil, + example: "Foo" + ) + ], + responses: .init(), + parameters: .init(), + examples: .init(), + requestBodies: [ + "foo": .init( + description: "foo", + content: [ + .json: .init( + schemaReference: .component(named: "schemaID") + ) + ], + required: true, + vendorExtensions: [:] + ) + ], + headers: .init(), + securitySchemes: .init(), + links: .init(), + callbacks: .init(), + vendorExtensions: .init() + ) + ) + + let encoder = YAMLEncoder() + + do { + _ = + try doc + .locallyDereferenced() + .resolved() + } + catch { + print(type(of: error)) + print(error) + throw error + } + + let result = try encoder.encode(doc) + print("---- 3.0 ----") + print(result) + + let doc31 = doc.convert(to: .v3_1_0) + let result31 = try encoder.encode(doc31) + print("---- 3.1 ----") + print(result31) + + let doc32 = doc.convert(to: .v3_2_0) + let result32 = try encoder.encode(doc32) + print("---- 3.2 ----") + print(result32) + + } +} diff --git a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift index 47a7609..6a5fccb 100644 --- a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift +++ b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift @@ -39,7 +39,9 @@ struct FeatherOpenAPIKitTests { return } - _ = try encoder.encode(openAPIDocument) + let output = try encoder.encode(openAPIDocument) + + print(output) } @Test From 73f5a1c875e356036088b66ffb22c290a34ea7f5 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Wed, 21 Jan 2026 21:48:19 +0100 Subject: [PATCH 08/34] experimental builder --- .../FeatherOpenAPI30/ComponentBuilder.swift | 115 +++++++++++++++++ Sources/FeatherOpenAPI30/Components.swift | 58 +++++---- .../Example/ExampleDocument.swift | 8 -- .../Example/ExampleHeaders.swift | 26 ---- .../Example/ExampleModel.swift | 67 ++++++++++ .../Example/ExampleOperations.swift | 24 ---- .../Example/ExampleParameters.swift | 24 ---- .../Example/ExamplePathItems.swift | 8 -- .../Example/ExampleRequestBodies.swift | 12 -- .../Example/ExampleResponses.swift | 12 -- .../Example/ExampleSchemas.swift | 25 ---- .../Example/ExampleSecuritySchemes.swift | 8 -- .../Example/ExampleTags.swift | 8 -- .../FeatherOpenAPIKitTests.swift | 121 ++++-------------- 14 files changed, 242 insertions(+), 274 deletions(-) create mode 100644 Sources/FeatherOpenAPI30/ComponentBuilder.swift delete mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExampleDocument.swift delete mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExampleHeaders.swift create mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExampleModel.swift delete mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExampleOperations.swift delete mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExampleParameters.swift delete mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExamplePathItems.swift delete mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExampleRequestBodies.swift delete mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExampleResponses.swift delete mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExampleSchemas.swift delete mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExampleSecuritySchemes.swift delete mode 100644 Tests/FeatherOpenAPI30Tests/Example/ExampleTags.swift diff --git a/Sources/FeatherOpenAPI30/ComponentBuilder.swift b/Sources/FeatherOpenAPI30/ComponentBuilder.swift new file mode 100644 index 0000000..3043d5d --- /dev/null +++ b/Sources/FeatherOpenAPI30/ComponentBuilder.swift @@ -0,0 +1,115 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public struct ComponentBuilder { + + public var components: Components + + public init( + components: Components = .init() + ) { + self.components = components + } + + public mutating func header( + id: String, + builder: (() -> HeaderRepresentable) + ) -> (id: HeaderID, value: HeaderRepresentable) { + let header = builder() + + components.headers[.init(id)] = header + + return (id: .init(id), value: header) + } + + public mutating func parameter( + id: String, + builder: (() -> ParameterRepresentable) + ) -> (id: ParameterID, value: ParameterRepresentable) { + let parameter = builder() + + components.parameters[.init(id)] = parameter + + return (id: .init(id), value: parameter) + } + + public mutating func schema( + id: String, + builder: (() -> SchemaRepresentable) + ) -> (id: SchemaID, value: SchemaRepresentable) { + let schema = builder() + + if components.schemas[.init(id)] != nil { + print("Schema `\(id)` is already registered...") + } + + components.schemas[.init(id)] = schema + + return (id: .init(id), value: schema) + } + + public mutating func response( + id: String, + builder: (() -> ResponseRepresentable) + ) -> (id: ResponseID, value: ResponseRepresentable) { + let response = builder() + + components.responses[.init(id)] = response + + return (id: .init(id), value: response) + } + + + public mutating func requestBody( + id: String, + builder: (() -> RequestBodyRepresentable) + ) -> (id: RequestBodyID, value: RequestBodyRepresentable) { + let requestBody = builder() + + components.requestBodies[.init(id)] = requestBody + + return (id: .init(id), value: requestBody) + } + + + public mutating func addSchema( + id: String, + builder: (() -> SchemaRepresentable) + ) -> (id: SchemaID, value: SchemaRepresentable) { + let schema = builder() + + components.schemas[.init(id)] = schema + + return (id: .init(id), value: schema) + } + +// mutating func getSchema( +// id: String +// ) throws(ComponentBuilderError) -> (id: SchemaID, value: SchemaRepresentable) { +// guard let schema = components.schemas[.init(id)] else { +// throw .missingSchema(id) +// } +// return (id: .init(id), value: schema) +// } +// +// mutating func getSchemaID( +// id: String +// ) throws(ComponentBuilderError) -> SchemaID { +// guard let schema = components.schemas[.init(id)] else { +// throw .missingSchema(id) +// } +// return (id: .init(id), value: schema) +// } +} + + + +//public enum ComponentBuilderError: Error { +// case missingSchema(String) +//} diff --git a/Sources/FeatherOpenAPI30/Components.swift b/Sources/FeatherOpenAPI30/Components.swift index 82ac42e..f4b60be 100644 --- a/Sources/FeatherOpenAPI30/Components.swift +++ b/Sources/FeatherOpenAPI30/Components.swift @@ -14,32 +14,24 @@ public protocol ComponentsRepresentable { public struct Components: ComponentsRepresentable { public var schemas: OrderedDictionary - public var parameters: - OrderedDictionary + public var parameters: OrderedDictionary public var examples: OrderedDictionary public var responses: OrderedDictionary - public var requestBodies: - OrderedDictionary + public var requestBodies: OrderedDictionary public var headers: OrderedDictionary - public var securitySchemes: - OrderedDictionary + public var securitySchemes: OrderedDictionary public var links: OrderedDictionary // public var callbacks: OrderedDictionary public var vendorExtensions: [String: AnyCodable] public init( schemas: OrderedDictionary = [:], - parameters: OrderedDictionary = - [:], + parameters: OrderedDictionary = [:], examples: OrderedDictionary = [:], responses: OrderedDictionary = [:], - requestBodies: OrderedDictionary< - RequestBodyID, RequestBodyRepresentable - > = [:], + requestBodies: OrderedDictionary = [:], headers: OrderedDictionary = [:], - securitySchemes: OrderedDictionary< - SecuritySchemeID, SecuritySchemeRepresentable - > = [:], + securitySchemes: OrderedDictionary = [:], links: OrderedDictionary = [:], // callbacks: OrderedDictionary = [:], vendorExtensions: [String: AnyCodable] = [:] @@ -56,7 +48,9 @@ public struct Components: ComponentsRepresentable { self.vendorExtensions = vendorExtensions } - func openAPISchemas() -> OpenAPI.ComponentDictionary { + func openAPISchemas( + + ) -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in schemas { @@ -65,7 +59,9 @@ public struct Components: ComponentsRepresentable { return result } - func openAPIResponses() -> OpenAPI.ComponentDictionary { + func openAPIResponses( + + ) -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in responses { @@ -74,7 +70,9 @@ public struct Components: ComponentsRepresentable { return result } - func openAPIParameters() -> OpenAPI.ComponentDictionary { + func openAPIParameters( + + ) -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in parameters { @@ -84,7 +82,9 @@ public struct Components: ComponentsRepresentable { return result } - func openAPIExamples() -> OpenAPI.ComponentDictionary { + func openAPIExamples( + + ) -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in examples { @@ -93,8 +93,9 @@ public struct Components: ComponentsRepresentable { return result } - func openAPIRequestBodies() -> OpenAPI.ComponentDictionary - { + func openAPIRequestBodies( + + ) -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in requestBodies { @@ -104,7 +105,9 @@ public struct Components: ComponentsRepresentable { return result } - func openAPIHeaders() -> OpenAPI.ComponentDictionary { + func openAPIHeaders( + + ) -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in headers { @@ -113,9 +116,9 @@ public struct Components: ComponentsRepresentable { return result } - func openAPISecuritySchemes() - -> OpenAPI.ComponentDictionary - { + func openAPISecuritySchemes( + + ) -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in securitySchemes { @@ -125,7 +128,9 @@ public struct Components: ComponentsRepresentable { return result } - func openAPILinks() -> OpenAPI.ComponentDictionary { + func openAPILinks( + + ) -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in links { @@ -157,4 +162,7 @@ public struct Components: ComponentsRepresentable { vendorExtensions: vendorExtensions ) } + + } + diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleDocument.swift b/Tests/FeatherOpenAPI30Tests/Example/ExampleDocument.swift deleted file mode 100644 index e60b094..0000000 --- a/Tests/FeatherOpenAPI30Tests/Example/ExampleDocument.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import FeatherOpenAPI30 diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleHeaders.swift b/Tests/FeatherOpenAPI30Tests/Example/ExampleHeaders.swift deleted file mode 100644 index b6e4607..0000000 --- a/Tests/FeatherOpenAPI30Tests/Example/ExampleHeaders.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import FeatherOpenAPI30 - -extension HeaderID { - static var example: Self { .init(#function) } -} - -extension HeaderID { - static var customResponse: Self { .init(#function) } -} - -extension Header { - - static var customResponse: Self { - .init( - schema: .customResponseHeader, - description: "My custom response header", - ) - } -} diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleModel.swift b/Tests/FeatherOpenAPI30Tests/Example/ExampleModel.swift new file mode 100644 index 0000000..c5d0005 --- /dev/null +++ b/Tests/FeatherOpenAPI30Tests/Example/ExampleModel.swift @@ -0,0 +1,67 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import FeatherOpenAPI30 +import OpenAPIKit30 + + +func exampleGETOperation( + using builder: inout ComponentBuilder +) -> Operation { + + let sch1 = builder.schema(id: "TestSchema") { + StringSchema() + } + +// let sch2 = builder.schema(id: "todo.id") { +// StringSchema() +// } + + let header = builder.header(id: "header") { + Header(schema: sch1.id) + } + + let p1 = builder.parameter(id: "foo") { + Parameter( + name: "foo", + context: .path, + schema: sch1.id + ) + } + + let reqBody1 = builder.requestBody(id: "foo") { + RequestBody( + description: "This is a proper request body", + content: [ + .json: Content(schema: sch1.id) + ] + ) + } + + let response1 = builder.response(id: "foo") { + Response( + description: "foo", + headers: [ + "X-Custom-Response-Header": header.id + ], + content: [ + .aac: Content(schema: sch1.id) + ] + ) + } + + return .init( + parameters: [ + p1.id, + ], + requestBody: reqBody1.id, + responses: [ + 200: response1.id + ] + ) +} + diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleOperations.swift b/Tests/FeatherOpenAPI30Tests/Example/ExampleOperations.swift deleted file mode 100644 index b0522af..0000000 --- a/Tests/FeatherOpenAPI30Tests/Example/ExampleOperations.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import FeatherOpenAPI30 -import OpenAPIKit30 - -extension Operation { - - static var foo: Self { - .init( - parameters: [ - .foo - ], - requestBody: .foo, - responses: [ - 200: .foo - ] - ) - } -} diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleParameters.swift b/Tests/FeatherOpenAPI30Tests/Example/ExampleParameters.swift deleted file mode 100644 index d58924e..0000000 --- a/Tests/FeatherOpenAPI30Tests/Example/ExampleParameters.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 -import FeatherOpenAPI30 - -extension ParameterID { - static var foo: Self { .init(#function) } -} - -extension Parameter { - - static var foo: Self { - Parameter( - name: "foo", - context: .path, - schema: .test - ) - } -} diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExamplePathItems.swift b/Tests/FeatherOpenAPI30Tests/Example/ExamplePathItems.swift deleted file mode 100644 index e60b094..0000000 --- a/Tests/FeatherOpenAPI30Tests/Example/ExamplePathItems.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import FeatherOpenAPI30 diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleRequestBodies.swift b/Tests/FeatherOpenAPI30Tests/Example/ExampleRequestBodies.swift deleted file mode 100644 index 5653785..0000000 --- a/Tests/FeatherOpenAPI30Tests/Example/ExampleRequestBodies.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import FeatherOpenAPI30 - -extension RequestBodyID { - static var foo: Self { .init(#function) } -} diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleResponses.swift b/Tests/FeatherOpenAPI30Tests/Example/ExampleResponses.swift deleted file mode 100644 index 3f84eed..0000000 --- a/Tests/FeatherOpenAPI30Tests/Example/ExampleResponses.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import FeatherOpenAPI30 - -extension ResponseID { - static var foo: Self { .init(#function) } -} diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleSchemas.swift b/Tests/FeatherOpenAPI30Tests/Example/ExampleSchemas.swift deleted file mode 100644 index 69bc308..0000000 --- a/Tests/FeatherOpenAPI30Tests/Example/ExampleSchemas.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import FeatherOpenAPI30 -import OpenAPIKit30 - -extension SchemaID { - static var test: Self { .init(#function) } - - static var customResponseHeader: Self { .init(#function) } -} - -extension StringSchema { - - static var customResponseHeader: Self { - .init( - description: "Custom header", - example: "my-example-key" - ) - } -} diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleSecuritySchemes.swift b/Tests/FeatherOpenAPI30Tests/Example/ExampleSecuritySchemes.swift deleted file mode 100644 index e60b094..0000000 --- a/Tests/FeatherOpenAPI30Tests/Example/ExampleSecuritySchemes.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import FeatherOpenAPI30 diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleTags.swift b/Tests/FeatherOpenAPI30Tests/Example/ExampleTags.swift deleted file mode 100644 index e60b094..0000000 --- a/Tests/FeatherOpenAPI30Tests/Example/ExampleTags.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import FeatherOpenAPI30 diff --git a/Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift index 8e07070..351150f 100644 --- a/Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift +++ b/Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift @@ -19,6 +19,10 @@ struct FeatherOpenAPIKitTests { @Test func example() throws { + + var builder = ComponentBuilder() + + let op1 = exampleGETOperation(using: &builder) let doc = Document( info: Info( @@ -27,49 +31,13 @@ struct FeatherOpenAPIKitTests { ), paths: [ "foo": PathItem( - get: Operation( - parameters: [ - .foo - ], - requestBody: .foo, - responses: [ - 200: .foo - ] - ) + summary: "foo bar baz", + get: op1 ) ], - components: Components( - schemas: [ - .test: StringSchema(), - .customResponseHeader: StringSchema.customResponseHeader, - ], - parameters: [ - .foo: Parameter.foo - ], - responses: [ - .foo: Response( - description: "foo", - headers: [ - "X-Custom-Response-Header": .customResponse - ], - content: [ - .aac: Content(schema: .test) - ], - ) - ], - requestBodies: [ - .foo: RequestBody( - content: [ - .any: Content(schema: .test) - ] - ) - ], - headers: [ - .customResponse: Header.customResponse - ] - ), + components: builder.components ) - + let openAPIdoc = doc.openAPIDocument() let encoder = YAMLEncoder() @@ -87,37 +55,11 @@ struct FeatherOpenAPIKitTests { let result = try encoder.encode(openAPIdoc) print(result) - + } - func renderTest() throws { - let paths: OpenAPIKit30.OpenAPI.PathItem.Map = [ - "foo": .init( - .init( - summary: "foo", - description: nil, - servers: nil, - parameters: [], - get: .init( - requestBody: .init( - .component( - named: "foo" - ) - ), - responses: [:] - ), - put: nil, - post: nil, - delete: nil, - options: nil, - head: nil, - patch: nil, - trace: nil, - vendorExtensions: [:] - ) - ) - ] + func renderTest() throws { let doc = OpenAPIKit30.OpenAPI.Document( info: .init( @@ -125,30 +67,28 @@ struct FeatherOpenAPIKitTests { version: "3.0.0" ), servers: [], - paths: paths, + paths: [ + "foo": .init( + .init( + summary: "foo", + get: .init( + requestBody: .init( + .component( + named: "foo" + ) + ), + responses: [:] + ), + ) + ) + ], components: .init( schemas: [ "schemaID": .string( format: .dateTime, - required: true, - nullable: false, - permissions: nil, - deprecated: nil, - title: nil, - description: nil, - discriminator: nil, - externalDocs: nil, - minLength: nil, - maxLength: nil, - pattern: nil, - allowedValues: nil, - defaultValue: nil, example: "Foo" - ) + ), ], - responses: .init(), - parameters: .init(), - examples: .init(), requestBodies: [ "foo": .init( description: "foo", @@ -156,16 +96,9 @@ struct FeatherOpenAPIKitTests { .json: .init( schemaReference: .component(named: "schemaID") ) - ], - required: true, - vendorExtensions: [:] + ] ) ], - headers: .init(), - securitySchemes: .init(), - links: .init(), - callbacks: .init(), - vendorExtensions: .init() ) ) From 1dc46a052b20c150db6fc5b53f49a232514e535c Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Wed, 21 Jan 2026 22:16:47 +0100 Subject: [PATCH 09/34] minor changes --- Sources/FeatherOpenAPI30/Operation.swift | 4 +- Sources/FeatherOpenAPI30/Schema.swift | 81 +++++++++++++++++++ .../Example/CreateExampleOperation.swift | 70 ++++++++++++++++ .../Example/GetExampleOperation.swift | 67 +++++++++++++++ ...ExampleModel.swift => TestOperation.swift} | 2 +- .../FeatherOpenAPIKitTests.swift | 10 ++- 6 files changed, 228 insertions(+), 6 deletions(-) create mode 100644 Tests/FeatherOpenAPI30Tests/Example/CreateExampleOperation.swift create mode 100644 Tests/FeatherOpenAPI30Tests/Example/GetExampleOperation.swift rename Tests/FeatherOpenAPI30Tests/Example/{ExampleModel.swift => TestOperation.swift} (98%) diff --git a/Sources/FeatherOpenAPI30/Operation.swift b/Sources/FeatherOpenAPI30/Operation.swift index 42d708d..a61e780 100644 --- a/Sources/FeatherOpenAPI30/Operation.swift +++ b/Sources/FeatherOpenAPI30/Operation.swift @@ -92,7 +92,9 @@ public struct Operation: OperationRepresentable { parameters: parameters.map { .reference(.component(named: $0.rawValue)) }, - responses: .init(), + responses: responses.mapValues { + .reference(.component(named: $0.rawValue)) + }, callbacks: .init(), deprecated: deprecated, security: security?.map { $0.openAPISecurityRequirement() }, diff --git a/Sources/FeatherOpenAPI30/Schema.swift b/Sources/FeatherOpenAPI30/Schema.swift index 03d8e53..eac514a 100644 --- a/Sources/FeatherOpenAPI30/Schema.swift +++ b/Sources/FeatherOpenAPI30/Schema.swift @@ -95,3 +95,84 @@ public struct StringSchema: SchemaRepresentable { ) } } + +public struct ObjectSchema: SchemaRepresentable { + + public var format: JSONTypeFormat.ObjectFormat + public var required: Bool + public var nullable: Bool? + // public var permissions: Permissions? + public var deprecated: Bool? + public var title: String? + public var description: String? + public var discriminator: OpenAPI.Discriminator? + public var externalDocs: OpenAPI.ExternalDocumentation? + public var minProperties: Int? + public var maxProperties: Int? + public var properties: OrderedDictionary +// public var properties: String? +// public var additionalProperties: String? + public var allowedValues: [AnyCodable]? + public var defaultValue: AnyCodable? + public var example: AnyCodable? + + public init( + format: JSONTypeFormat.ObjectFormat = .generic, + required: Bool = false, + nullable: Bool? = nil, + // permissions: Permissions? = nil, + deprecated: Bool? = nil, + title: String? = nil, + description: String? = nil, + discriminator: OpenAPI.Discriminator? = nil, + externalDocs: OpenAPI.ExternalDocumentation? = nil, + minProperties: Int? = nil, + maxProperties: Int? = nil, + properties: OrderedDictionary, + pattern: String? = nil, + allowedValues: [AnyCodable]? = nil, + defaultValue: AnyCodable? = nil, + example: AnyCodable? = nil + ) { + self.format = format + self.required = required + self.nullable = nullable + // self.permissions = permissions + self.deprecated = deprecated + self.title = title + self.description = description + self.discriminator = discriminator + self.externalDocs = externalDocs + self.minProperties = minProperties + self.maxProperties = maxProperties + self.properties = properties +// self.pattern = pattern + self.allowedValues = allowedValues + self.defaultValue = defaultValue + self.example = example + } + + public func openAPISchema() -> JSONSchema { + .object( + format: format, + required: required, + nullable: nullable, + permissions: nil, + deprecated: deprecated, + title: title, + description: description, + discriminator: discriminator, + externalDocs: externalDocs, + minProperties: minProperties, + maxProperties: maxProperties, + properties: properties.mapValues { $0.openAPISchema() }, +// properties: [ +// "asf": .reference(.component(named: "")), +// ], + additionalProperties: nil, + allowedValues: allowedValues, + defaultValue: defaultValue, + example: example + ) + } +} diff --git a/Tests/FeatherOpenAPI30Tests/Example/CreateExampleOperation.swift b/Tests/FeatherOpenAPI30Tests/Example/CreateExampleOperation.swift new file mode 100644 index 0000000..1359d65 --- /dev/null +++ b/Tests/FeatherOpenAPI30Tests/Example/CreateExampleOperation.swift @@ -0,0 +1,70 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import FeatherOpenAPI30 +import OpenAPIKit30 + + +func createExample( + using builder: inout ComponentBuilder +) -> Operation { + + let sch1 = builder.schema(id: "TestSchema") { + StringSchema() + } + +// let sch2 = builder.schema(id: "todo.id") { +// StringSchema() +// } + + let header = builder.header(id: "header") { + Header(schema: sch1.id) + } + + let p1 = builder.parameter(id: "foo") { + Parameter( + name: "foo", + context: .path, + schema: sch1.id + ) + } + + let reqBody1 = builder.requestBody(id: "foo") { + RequestBody( + description: "This is a proper request body", + content: [ + .json: Content(schema: sch1.id) + ] + ) + } + + let okResponse = builder.response(id: "foo") { + Response( + description: "foo", + headers: [ + "X-Custom-Response-Header": header.id + ], + content: [ + .aac: Content(schema: sch1.id) + ] + ) + } + + return .init( + tags: ["main"], + summary: "Detail example", + description: "Detail example detail", + parameters: [ + p1.id, + ], + requestBody: reqBody1.id, + responses: [ + 200: okResponse.id + ] + ) +} + diff --git a/Tests/FeatherOpenAPI30Tests/Example/GetExampleOperation.swift b/Tests/FeatherOpenAPI30Tests/Example/GetExampleOperation.swift new file mode 100644 index 0000000..682f8d8 --- /dev/null +++ b/Tests/FeatherOpenAPI30Tests/Example/GetExampleOperation.swift @@ -0,0 +1,67 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import FeatherOpenAPI30 +import OpenAPIKit30 + + +func getExample( + using builder: inout ComponentBuilder +) -> Operation { + + let idSchema = builder.schema(id: "ExampleId") { + StringSchema() + } + + let titleSchema = builder.schema(id: "ExampleTitle") { + StringSchema() + } + + let detailSchema = builder.schema(id: "ExampleDetail") { + ObjectSchema( + properties: [ + "id": StringSchema(), + "title": StringSchema(), + ] + ) + } + + + + let idParameter = builder.parameter(id: "ExampleId") { + Parameter( + name: "id", + context: .path, + schema: idSchema.id + ) + } + + let okResponse = builder.response(id: "GetExampleResponse") { + Response( + description: "Example detail response", +// headers: [ +// "X-Custom-Response-Header": header.id +// ], + content: [ + .aac: Content(schema: detailSchema.id) + ] + ) + } + + return .init( + tags: ["main"], + summary: "Detail example", + description: "Detail example detail", + parameters: [ + idParameter.id, + ], + responses: [ + 200: okResponse.id + ] + ) +} + diff --git a/Tests/FeatherOpenAPI30Tests/Example/ExampleModel.swift b/Tests/FeatherOpenAPI30Tests/Example/TestOperation.swift similarity index 98% rename from Tests/FeatherOpenAPI30Tests/Example/ExampleModel.swift rename to Tests/FeatherOpenAPI30Tests/Example/TestOperation.swift index c5d0005..47acc0f 100644 --- a/Tests/FeatherOpenAPI30Tests/Example/ExampleModel.swift +++ b/Tests/FeatherOpenAPI30Tests/Example/TestOperation.swift @@ -9,7 +9,7 @@ import FeatherOpenAPI30 import OpenAPIKit30 -func exampleGETOperation( +func testOperation( using builder: inout ComponentBuilder ) -> Operation { diff --git a/Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift index 351150f..70762a8 100644 --- a/Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift +++ b/Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift @@ -22,7 +22,8 @@ struct FeatherOpenAPIKitTests { var builder = ComponentBuilder() - let op1 = exampleGETOperation(using: &builder) + let getExampleOperation = getExample(using: &builder) +// let createExampleOperation = createExample(using: &builder) let doc = Document( info: Info( @@ -30,9 +31,10 @@ struct FeatherOpenAPIKitTests { version: "1.0.0" ), paths: [ - "foo": PathItem( - summary: "foo bar baz", - get: op1 + "examples": PathItem( + summary: "Example related operations", + get: getExampleOperation, +// post: createExampleOperation ) ], components: builder.components From 30b482a24ec3c0733333fbdb9f69a5d7f0162ec0 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Thu, 22 Jan 2026 11:23:45 +0100 Subject: [PATCH 10/34] downgrade old framework to openapikit30 --- .../Extensions/JSONSchema+Enumeration.swift | 6 +- .../Extensions/JSONSchema+Text.swift | 6 +- .../OrderedDictionary+Composition.swift | 2 +- .../FeatherOpenAPI/Interfaces/Document.swift | 22 ++--- .../Interfaces/Header/Header.swift | 12 +-- .../Interfaces/Identifiable.swift | 2 +- .../Interfaces/Operation/Operation.swift | 10 ++- .../Parameter/HeaderParameter.swift | 26 +++--- .../Interfaces/Parameter/Parameter.swift | 18 ++-- .../Interfaces/Parameter/PathParameter.swift | 10 ++- .../Interfaces/Parameter/QueryParameter.swift | 26 +++--- .../Interfaces/PathItem/PathItem.swift | 4 +- .../Interfaces/RequestBody/BinaryBody.swift | 10 +-- .../Interfaces/RequestBody/FormBody.swift | 10 ++- .../Interfaces/RequestBody/JSONBody.swift | 8 +- .../Interfaces/RequestBody/RequestBody.swift | 2 +- .../Interfaces/Response/BinaryResponse.swift | 16 ++-- .../Interfaces/Response/JSONResponse.swift | 2 +- .../Interfaces/Response/Response.swift | 20 ++--- .../Interfaces/Schema/ArraySchema.swift | 2 +- .../Interfaces/Schema/BooleanSchema.swift | 4 +- .../Interfaces/Schema/DateTimeSchema.swift | 8 +- .../Interfaces/Schema/DoubleSchema.swift | 6 +- .../Interfaces/Schema/EmailSchema.swift | 10 +-- .../Interfaces/Schema/EnumSchema.swift | 8 +- .../Interfaces/Schema/FloatSchema.swift | 6 +- .../Interfaces/Schema/Int32Schema.swift | 6 +- .../Interfaces/Schema/Int64Schema.swift | 6 +- .../Interfaces/Schema/IntSchema.swift | 6 +- .../Interfaces/Schema/NanoIDSchema.swift | 16 ++-- .../Interfaces/Schema/NumberSchema.swift | 4 +- .../Interfaces/Schema/ObjectSchema.swift | 2 +- .../Schema/ObjectSchemaProperty.swift | 2 +- .../Interfaces/Schema/PasswordSchema.swift | 8 +- .../Interfaces/Schema/Schema.swift | 2 +- .../Interfaces/Schema/TextSchema.swift | 8 +- .../Interfaces/Schema/UUIDSchema.swift | 18 ++-- .../SecurityScheme/SecurityScheme.swift | 2 +- .../FeatherOpenAPI/Interfaces/Tag/Tag.swift | 2 +- Sources/FeatherOpenAPI30/Callback.swift | 8 -- .../FeatherOpenAPI30/ComponentBuilder.swift | 88 +++++++++---------- Sources/FeatherOpenAPI30/Components.swift | 65 +++++++------- Sources/FeatherOpenAPI30/Schema.swift | 18 ++-- .../Example/CreateExampleOperation.swift | 46 +++++++--- .../Example/GetExampleOperation.swift | 51 +++++++---- .../Example/TestOperation.swift | 19 ++-- .../FeatherOpenAPIKitTests.swift | 35 ++++++-- .../Model/ExampleModel+Operations.swift | 1 - .../Model/ExampleModel+Responses.swift | 2 +- .../Example/Model/ExampleModel+Schemas.swift | 8 +- .../Model/ExampleModel+SecuritySchemes.swift | 2 +- .../ExampleDuplicatedItemModel+Schemas.swift | 9 +- ...xampleMissingParentItemModel+Schemas.swift | 4 +- .../Example/ExampleDocument.swift | 2 +- .../ExampleDuplicatedItemDocument.swift | 2 +- .../ExampleMissingParentItemDocument.swift | 2 +- .../FeatherOpenAPIKitTests.swift | 2 +- 57 files changed, 362 insertions(+), 340 deletions(-) diff --git a/Sources/FeatherOpenAPI/Extensions/JSONSchema+Enumeration.swift b/Sources/FeatherOpenAPI/Extensions/JSONSchema+Enumeration.swift index 22e7ac4..ef976a5 100644 --- a/Sources/FeatherOpenAPI/Extensions/JSONSchema+Enumeration.swift +++ b/Sources/FeatherOpenAPI/Extensions/JSONSchema+Enumeration.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 10/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public extension JSONSchema { @@ -13,14 +13,14 @@ public extension JSONSchema { description: String, allowedValues: [AnyCodable], defaultValue: AnyCodable? = nil, - examples: [AnyCodable] = [] + example: AnyCodable? = nil ) -> JSONSchema { .string( format: .generic, description: description, allowedValues: allowedValues, defaultValue: defaultValue, - examples: examples + example: example ) } } diff --git a/Sources/FeatherOpenAPI/Extensions/JSONSchema+Text.swift b/Sources/FeatherOpenAPI/Extensions/JSONSchema+Text.swift index 28bfeb0..b165093 100644 --- a/Sources/FeatherOpenAPI/Extensions/JSONSchema+Text.swift +++ b/Sources/FeatherOpenAPI/Extensions/JSONSchema+Text.swift @@ -5,18 +5,18 @@ // Created by Tibor Bodecs on 25/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public extension JSONSchema { static func text( description: String, - examples: [AnyCodable] = [] + example: AnyCodable? = nil ) -> Self { .string( format: .generic, description: description, - examples: examples + example: example ) } diff --git a/Sources/FeatherOpenAPI/Extensions/OrderedDictionary+Composition.swift b/Sources/FeatherOpenAPI/Extensions/OrderedDictionary+Composition.swift index 20496d5..fc8767e 100644 --- a/Sources/FeatherOpenAPI/Extensions/OrderedDictionary+Composition.swift +++ b/Sources/FeatherOpenAPI/Extensions/OrderedDictionary+Composition.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 10/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public extension OrderedDictionary { diff --git a/Sources/FeatherOpenAPI/Interfaces/Document.swift b/Sources/FeatherOpenAPI/Interfaces/Document.swift index cea95db..5f213b7 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Document.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Document.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import OpenAPIKit +import OpenAPIKit30 import OpenAPIKitCore // https://spec.openapis.org/oas/latest.html @@ -16,15 +16,15 @@ public protocol Document { func schemas() throws -> OpenAPI.ComponentDictionary func parameters() throws - -> OpenAPI.ComponentReferenceDictionary + -> OpenAPI.ComponentDictionary func headers() throws - -> OpenAPI.ComponentReferenceDictionary + -> OpenAPI.ComponentDictionary func requestBodies() throws - -> OpenAPI.ComponentReferenceDictionary + -> OpenAPI.ComponentDictionary func securitySchemes() throws - -> OpenAPI.ComponentReferenceDictionary + -> OpenAPI.ComponentDictionary func responses() throws - -> OpenAPI.ComponentReferenceDictionary + -> OpenAPI.ComponentDictionary func tags() throws -> [OpenAPI.Tag] func paths() throws -> OpenAPI.PathItem.Map @@ -86,7 +86,7 @@ public extension Document { } func parameters() throws - -> OpenAPI.ComponentReferenceDictionary + -> OpenAPI.ComponentDictionary { return try Self.filterIdentifiables( @@ -100,7 +100,7 @@ public extension Document { } func headers() throws - -> OpenAPI.ComponentReferenceDictionary + -> OpenAPI.ComponentDictionary { return try Self.filterIdentifiables( @@ -114,7 +114,7 @@ public extension Document { } func requestBodies() throws - -> OpenAPI.ComponentReferenceDictionary + -> OpenAPI.ComponentDictionary { return try Self.filterIdentifiables( @@ -128,7 +128,7 @@ public extension Document { } func securitySchemes() throws - -> OpenAPI.ComponentReferenceDictionary + -> OpenAPI.ComponentDictionary { return try Self.filterIdentifiables( @@ -142,7 +142,7 @@ public extension Document { } func responses() throws - -> OpenAPI.ComponentReferenceDictionary + -> OpenAPI.ComponentDictionary { return diff --git a/Sources/FeatherOpenAPI/Interfaces/Header/Header.swift b/Sources/FeatherOpenAPI/Interfaces/Header/Header.swift index 5b1746e..4cee726 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Header/Header.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Header/Header.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol OpenAPIHeader: Identifiable { static func openAPIHeader() -> OpenAPI.Header @@ -19,11 +19,11 @@ public protocol Header: OpenAPIHeader { public extension Header { - static func reference() -> Either< - OpenAPI.Reference, OpenAPI.Header - > { - .reference(.component(named: id)) - } + // static func reference() -> Either< + // OpenAPI.Reference, OpenAPI.Header + // > { + // .reference(.component(named: id)) + // } } public extension Header { diff --git a/Sources/FeatherOpenAPI/Interfaces/Identifiable.swift b/Sources/FeatherOpenAPI/Interfaces/Identifiable.swift index 274fed4..1fb4f25 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Identifiable.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Identifiable.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol Identifiable { static var id: String { get } diff --git a/Sources/FeatherOpenAPI/Interfaces/Operation/Operation.swift b/Sources/FeatherOpenAPI/Interfaces/Operation/Operation.swift index eeffe2a..5d64659 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Operation/Operation.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Operation/Operation.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 25/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol OpenAPIOperation: Identifiable { static func openAPIOperation() -> OpenAPI.Operation @@ -48,11 +48,13 @@ public extension Operation { summary: summary, description: description, operationId: operationId, - parameters: parameters.map { $0.reference() }, + parameters: parameters.map { .reference(.component(named: $0.id)) }, requestBody: requestBody?.openAPIRequestBody(), responses: responses.reduce(into: [:]) { - $0[.init(integerLiteral: $1.statusCode)] = $1.response - .reference() + $0[.init(integerLiteral: $1.statusCode)] = .reference( + .component(named: $1.response.id) + ) + // .reference() }, security: security.map { [$0.reference(): []] } ) diff --git a/Sources/FeatherOpenAPI/Interfaces/Parameter/HeaderParameter.swift b/Sources/FeatherOpenAPI/Interfaces/Parameter/HeaderParameter.swift index 8c8fe37..42ed81a 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Parameter/HeaderParameter.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Parameter/HeaderParameter.swift @@ -5,23 +5,25 @@ // Created by Tibor Bodecs on 15/02/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol HeaderParameter: Parameter {} public extension HeaderParameter { + // TODO: static var context: OpenAPI.Parameter.Context { - .header( - required: Self.required, - schemaOrContent: .schema( - .schema( - Self.schema.openAPISchema(), - style: .default( - for: .header - ) - ) - ) - ) + .header(required: Self.required) + // .header( + // required: Self.required, + // schemaOrContent: .schema( + // .schema( + // Self.schema.openAPISchema(), + // style: .default( + // for: .header + // ) + // ) + // ) + // ) } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Parameter/Parameter.swift b/Sources/FeatherOpenAPI/Interfaces/Parameter/Parameter.swift index e858692..8676557 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Parameter/Parameter.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Parameter/Parameter.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol OpenAPIParameter: Identifiable { static func openAPIParameter() -> OpenAPI.Parameter @@ -13,12 +13,12 @@ public protocol OpenAPIParameter: Identifiable { public extension OpenAPIParameter { - static func reference() -> Either< - OpenAPI.Reference, - OpenAPI.Parameter - > { - .reference(.component(named: id)) - } + // static func reference() -> Either< + // OpenAPI.Reference, + // OpenAPI.Parameter + // > { + // .reference(.component(named: id)) + // } } public protocol Parameter: OpenAPIParameter { @@ -38,8 +38,8 @@ public extension Parameter { static func openAPIParameter() -> OpenAPI.Parameter { .init( name: name, - context: context, // TODO: fix me - // schema: schema.reference(), + context: context, + schema: schema.reference(), description: description ) } diff --git a/Sources/FeatherOpenAPI/Interfaces/Parameter/PathParameter.swift b/Sources/FeatherOpenAPI/Interfaces/Parameter/PathParameter.swift index b429148..8dc86bb 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Parameter/PathParameter.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Parameter/PathParameter.swift @@ -5,15 +5,17 @@ // Created by Tibor Bodecs on 25/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol PathParameter: Parameter {} public extension PathParameter { + // TODO: static var context: OpenAPI.Parameter.Context { - .path( - schema: Self.schema.openAPISchema() - ) + .path + // .path( + // schema: Self.schema.openAPISchema() + // ) } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Parameter/QueryParameter.swift b/Sources/FeatherOpenAPI/Interfaces/Parameter/QueryParameter.swift index 3ca31c5..87c6f94 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Parameter/QueryParameter.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Parameter/QueryParameter.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 25/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol QueryParameter: Parameter {} @@ -13,17 +13,19 @@ public extension QueryParameter { static var required: Bool { false } + // TODO: static var context: OpenAPI.Parameter.Context { - .query( - required: Self.required, - schemaOrContent: .schema( - .schema( - Self.schema.openAPISchema(), - style: .default( - for: .query - ) - ) - ) - ) + .query(required: Self.required) + // .query( + // required: Self.required, + // schemaOrContent: .schema( + // .schema( + // Self.schema.openAPISchema(), + // style: .default( + // for: .query + // ) + // ) + // ) + // ) } } diff --git a/Sources/FeatherOpenAPI/Interfaces/PathItem/PathItem.swift b/Sources/FeatherOpenAPI/Interfaces/PathItem/PathItem.swift index 8654d16..d516a58 100644 --- a/Sources/FeatherOpenAPI/Interfaces/PathItem/PathItem.swift +++ b/Sources/FeatherOpenAPI/Interfaces/PathItem/PathItem.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol OpenAPIPathItem: Identifiable { static var openAPIPath: OpenAPI.Path { get } @@ -52,7 +52,7 @@ public extension PathItem { summary: summary, description: description, servers: nil, - parameters: parameters.map { $0.reference() }, + parameters: parameters.map { .reference(.component(named: $0.id)) }, get: get?.openAPIOperation(), put: put?.openAPIOperation(), post: post?.openAPIOperation(), diff --git a/Sources/FeatherOpenAPI/Interfaces/RequestBody/BinaryBody.swift b/Sources/FeatherOpenAPI/Interfaces/RequestBody/BinaryBody.swift index bfdb2c2..635922d 100644 --- a/Sources/FeatherOpenAPI/Interfaces/RequestBody/BinaryBody.swift +++ b/Sources/FeatherOpenAPI/Interfaces/RequestBody/BinaryBody.swift @@ -1,4 +1,4 @@ -import OpenAPIKit +import OpenAPIKit30 public protocol BinaryBody: RequestBody { static var contentType: OpenAPI.ContentType { get } @@ -15,11 +15,9 @@ extension BinaryBody { description: description, content: [ contentType: .init( - .init( - schema: .string( - contentMediaType: .other("application/octet-stream") - ), - examples: nil + schema: .string( + format: .binary, + // contentMediaType: .other("application/octet-stream") ) ) ], diff --git a/Sources/FeatherOpenAPI/Interfaces/RequestBody/FormBody.swift b/Sources/FeatherOpenAPI/Interfaces/RequestBody/FormBody.swift index 49d87ae..1f5a3b0 100644 --- a/Sources/FeatherOpenAPI/Interfaces/RequestBody/FormBody.swift +++ b/Sources/FeatherOpenAPI/Interfaces/RequestBody/FormBody.swift @@ -4,7 +4,7 @@ // Created by gerp83 on 28/08/2024 // -import OpenAPIKit +import OpenAPIKit30 public protocol FormBody: RequestBody { static var description: String { get } @@ -20,9 +20,13 @@ public extension FormBody { .init( description: description, content: [ - .form: .init(schema.reference()) + .form: .init( + schemaReference: .component( + named: schema.id + ) + ) ], - required: required + required: required, ) } } diff --git a/Sources/FeatherOpenAPI/Interfaces/RequestBody/JSONBody.swift b/Sources/FeatherOpenAPI/Interfaces/RequestBody/JSONBody.swift index 4e64daa..3bc256d 100644 --- a/Sources/FeatherOpenAPI/Interfaces/RequestBody/JSONBody.swift +++ b/Sources/FeatherOpenAPI/Interfaces/RequestBody/JSONBody.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 25/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol JSONBody: RequestBody { static var description: String { get } @@ -21,7 +21,11 @@ public extension JSONBody { .init( description: description, content: [ - .json: .init(schema.reference()) + .json: .init( + schemaReference: .component( + named: schema.id + ) + ) ], required: required ) diff --git a/Sources/FeatherOpenAPI/Interfaces/RequestBody/RequestBody.swift b/Sources/FeatherOpenAPI/Interfaces/RequestBody/RequestBody.swift index e8662df..2932c89 100644 --- a/Sources/FeatherOpenAPI/Interfaces/RequestBody/RequestBody.swift +++ b/Sources/FeatherOpenAPI/Interfaces/RequestBody/RequestBody.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol OpenAPIRequestBody: Identifiable { static func openAPIRequestBody() -> OpenAPI.Request diff --git a/Sources/FeatherOpenAPI/Interfaces/Response/BinaryResponse.swift b/Sources/FeatherOpenAPI/Interfaces/Response/BinaryResponse.swift index 45971d3..f9ce780 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Response/BinaryResponse.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Response/BinaryResponse.swift @@ -1,4 +1,4 @@ -import OpenAPIKit +import OpenAPIKit30 public protocol BinaryResponse: Response {} @@ -6,21 +6,17 @@ public extension BinaryResponse { static func openAPIResponse() -> OpenAPI.Response { .init( - summary: nil, description: description, headers: openAPIHeaderMap(), content: openAPIContentMap() + [ .any: .init( - .init( - schema: .string( - contentMediaType: .other("application/octet-stream") - ), - examples: nil + schema: .string( + format: .binary + // contentMediaType: .other("application/octet-stream") ) ) - ], - links: .init(), - vendorExtensions: [:] + ] + ) } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Response/JSONResponse.swift b/Sources/FeatherOpenAPI/Interfaces/Response/JSONResponse.swift index 2e01680..df4ca4b 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Response/JSONResponse.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Response/JSONResponse.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 25/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol JSONResponse: Response { diff --git a/Sources/FeatherOpenAPI/Interfaces/Response/Response.swift b/Sources/FeatherOpenAPI/Interfaces/Response/Response.swift index 43922cc..b981b47 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Response/Response.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Response/Response.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol OpenAPIResponse: Identifiable { static func openAPIResponse() -> OpenAPI.Response @@ -13,12 +13,12 @@ public protocol OpenAPIResponse: Identifiable { public extension OpenAPIResponse { - static func reference() -> Either< - OpenAPI.Reference, - OpenAPI.Response - > { - .reference(.component(named: id)) - } + // static func reference() -> Either< + // OpenAPI.Reference, + // OpenAPI.Response + // > { + // .reference(.component(named: id)) + // } } public protocol Response: OpenAPIResponse { @@ -31,9 +31,7 @@ public extension Response { static var headers: [Header.Type] { [] } - static var contents: - [OpenAPIKit.OpenAPI.ContentType: FeatherOpenAPI.Schema.Type] - { + static var contents: [OpenAPI.ContentType: FeatherOpenAPI.Schema.Type] { [:] } @@ -59,7 +57,7 @@ public extension Response { } var result: OpenAPI.Header.Map = [:] for header in headers { - result[header.id] = header.reference() + result[header.id] = .reference(.component(named: header.id)) } return result } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/ArraySchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/ArraySchema.swift index 5b5bec6..70f033f 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/ArraySchema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/ArraySchema.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 25/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol ArraySchema: Schema { static var minItems: Int? { get } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/BooleanSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/BooleanSchema.swift index 10683d5..7305d47 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/BooleanSchema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/BooleanSchema.swift @@ -1,11 +1,11 @@ -import OpenAPIKit +import OpenAPIKit30 public protocol BooleanSchema: Schema { static var defaultValue: Bool { get } } extension BooleanSchema { - public static func openAPISchema() -> OpenAPIKit.JSONSchema { + public static func openAPISchema() -> JSONSchema { .boolean( description: description, defaultValue: .init(defaultValue) diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/DateTimeSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/DateTimeSchema.swift index 7898efc..3112837 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/DateTimeSchema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/DateTimeSchema.swift @@ -1,17 +1,17 @@ -import OpenAPIKit +import OpenAPIKit30 public protocol DateTimeSchema: Schema { - static var examples: [String] { get } + static var example: String { get } } extension DateTimeSchema { public static var examples: [String] { ["2023-04-04T09:20:15.000Z"] } - public static func openAPISchema() -> OpenAPIKit.JSONSchema { + public static func openAPISchema() -> JSONSchema { .string( format: .dateTime, description: description, - examples: examples.map { .init(stringLiteral: $0) } + example: .init(stringLiteral: example) ) } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/DoubleSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/DoubleSchema.swift index 827e10e..e21547e 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/DoubleSchema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/DoubleSchema.swift @@ -1,4 +1,4 @@ -import OpenAPIKit +import OpenAPIKit30 public protocol DoubleSchema: NumberSchema { associatedtype T = Double @@ -6,7 +6,7 @@ public protocol DoubleSchema: NumberSchema { extension DoubleSchema where T == Double { - public static func openAPISchema() -> OpenAPIKit.JSONSchema { + public static func openAPISchema() -> JSONSchema { .number( format: .double, required: true, @@ -14,7 +14,7 @@ extension DoubleSchema where T == Double { maximum: maximum, minimum: minimum, defaultValue: defaultValue.map { .init($0) }, - examples: examples.map { .init($0) } + example: example.map { .init($0) } ) } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/EmailSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/EmailSchema.swift index 87baa24..df3d2d0 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/EmailSchema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/EmailSchema.swift @@ -1,16 +1,16 @@ -import OpenAPIKit +import OpenAPIKit30 public protocol EmailSchema: Schema { - static var examples: [String] { get } + static var example: String? { get } } extension EmailSchema { - public static func openAPISchema() -> OpenAPIKit.JSONSchema { + public static func openAPISchema() -> JSONSchema { .string( - format: .email, + format: .generic, required: true, description: description, - examples: examples.map { .init($0) } + example: example.map { .init($0) } ) } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/EnumSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/EnumSchema.swift index 5b0472f..1559759 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/EnumSchema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/EnumSchema.swift @@ -5,17 +5,17 @@ // Created by Tibor Bodecs on 25/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol EnumSchema: Schema { static var allowedValues: [String] { get } static var defaultValue: String? { get } - static var examples: [String] { get } + static var example: String? { get } } public extension EnumSchema { static var defaultValue: String? { nil } - static var examples: [String] { [] } + static var example: String? { nil } } public extension EnumSchema { @@ -29,7 +29,7 @@ public extension EnumSchema { description: description, allowedValues: allowedValues.map { .init(stringLiteral: $0) }, defaultValue: anyDefault, - examples: examples.map { .init(stringLiteral: $0) } + example: example.map { .init(stringLiteral: $0) } ) } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/FloatSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/FloatSchema.swift index 4de1a5e..929bd49 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/FloatSchema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/FloatSchema.swift @@ -1,4 +1,4 @@ -import OpenAPIKit +import OpenAPIKit30 public protocol FloatSchema: NumberSchema { associatedtype T = Float @@ -6,7 +6,7 @@ public protocol FloatSchema: NumberSchema { extension FloatSchema where T == Float { - public static func openAPISchema() -> OpenAPIKit.JSONSchema { + public static func openAPISchema() -> JSONSchema { .number( format: .float, required: true, @@ -14,7 +14,7 @@ extension FloatSchema where T == Float { maximum: maximum.map { (Double($0.0), exclusive: $0.1) }, minimum: minimum.map { (Double($0.0), exclusive: $0.1) }, defaultValue: defaultValue.map { .init($0) }, - examples: examples.map { .init($0) } + example: example.map { .init($0) } ) } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/Int32Schema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/Int32Schema.swift index 7b22b08..7f0f75d 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/Int32Schema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/Int32Schema.swift @@ -1,4 +1,4 @@ -import OpenAPIKit +import OpenAPIKit30 public protocol Int32Schema: NumberSchema { associatedtype T = Int32 @@ -6,7 +6,7 @@ public protocol Int32Schema: NumberSchema { extension Int32Schema where T == Int32 { - public static func openAPISchema() -> OpenAPIKit.JSONSchema { + public static func openAPISchema() -> JSONSchema { .integer( format: .int32, required: true, @@ -17,7 +17,7 @@ extension Int32Schema where T == Int32 { minimum: maximum.map { (Int($0.0), exclusive: $0.exclusive) }, defaultValue: defaultValue.map { .init($0) }, - examples: examples.map { .init($0) } + example: example.map { .init($0) } ) } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/Int64Schema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/Int64Schema.swift index 6bad6e1..0b9e0d6 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/Int64Schema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/Int64Schema.swift @@ -1,4 +1,4 @@ -import OpenAPIKit +import OpenAPIKit30 public protocol Int64Schema: NumberSchema { //TODO: use int64, remove Int cast after openapi kit fixed @@ -8,7 +8,7 @@ public protocol Int64Schema: NumberSchema { //TODO: use int64, remove Int cast after openapi kit fixed extension Int64Schema where T == Int { - public static func openAPISchema() -> OpenAPIKit.JSONSchema { + public static func openAPISchema() -> JSONSchema { .integer( format: .int64, required: true, @@ -16,7 +16,7 @@ extension Int64Schema where T == Int { maximum: maximum, minimum: minimum, defaultValue: defaultValue.map { .init($0) }, - examples: examples.map { .init($0) } + example: example.map { .init($0) } ) } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/IntSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/IntSchema.swift index 217466a..8f42608 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/IntSchema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/IntSchema.swift @@ -1,4 +1,4 @@ -import OpenAPIKit +import OpenAPIKit30 public protocol IntSchema: NumberSchema { associatedtype T = Int @@ -6,7 +6,7 @@ public protocol IntSchema: NumberSchema { extension IntSchema where T == Int { - public static func openAPISchema() -> OpenAPIKit.JSONSchema { + public static func openAPISchema() -> JSONSchema { .integer( format: .unspecified, required: true, @@ -14,7 +14,7 @@ extension IntSchema where T == Int { maximum: maximum, minimum: minimum, defaultValue: defaultValue.map { .init(integerLiteral: $0) }, - examples: examples.map { .init(integerLiteral: $0) } + example: example.map { .init(integerLiteral: $0) } ) } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/NanoIDSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/NanoIDSchema.swift index b156518..2d63bfc 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/NanoIDSchema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/NanoIDSchema.swift @@ -5,29 +5,23 @@ // Created by Tibor Bodecs on 16/03/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol NanoIDSchema: Schema { - static var examples: [String] { get } + static var example: String? { get } } public extension NanoIDSchema { - static var examples: [String] { - [ - "xHVX15b8z_wQDPH93uVp5", - "n4a9MyIfbqED76LPz4EaL", - "9eo5rQfE4I8ldgk5JLvZW", - "ZtmPFlB6FFU16suLM-LVf", - "mGUz4JpOVWYwpN2pwjYk9", - ] + static var example: String? { + "xHVX15b8z_wQDPH93uVp5" } static func openAPISchema() -> JSONSchema { .string( format: .generic, description: description, - examples: examples.map { .init($0) } + example: example.map { .init($0) } ) } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/NumberSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/NumberSchema.swift index 25b9d71..4c6fcb0 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/NumberSchema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/NumberSchema.swift @@ -2,7 +2,7 @@ public protocol NumberSchema: Schema { associatedtype T static var defaultValue: T? { get } - static var examples: [T] { get } + static var example: T? { get } static var minimumValue: T? { get } static var minimumExclusive: Bool { get } @@ -33,5 +33,5 @@ extension NumberSchema { } public static var defaultValue: T? { nil } - public static var examples: [T] { [] } + public static var example: T? { nil } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/ObjectSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/ObjectSchema.swift index da5415e..381c5ce 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/ObjectSchema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/ObjectSchema.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 25/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol ObjectSchema: Schema { static var properties: [ObjectSchemaProperty] { get } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/ObjectSchemaProperty.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/ObjectSchemaProperty.swift index 9839b29..7e83742 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/ObjectSchemaProperty.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/ObjectSchemaProperty.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 25/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public struct ObjectSchemaProperty { diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/PasswordSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/PasswordSchema.swift index ebf45b2..1567f88 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/PasswordSchema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/PasswordSchema.swift @@ -1,16 +1,16 @@ -import OpenAPIKit +import OpenAPIKit30 public protocol PasswordSchema: Schema { - static var examples: [String] { get } + static var example: String? { get } } extension PasswordSchema { - public static func openAPISchema() -> OpenAPIKit.JSONSchema { + public static func openAPISchema() -> JSONSchema { .string( format: .password, required: true, description: description, - examples: examples.map { .init($0) } + example: example.map { .init($0) } ) } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/Schema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/Schema.swift index 97f75da..957a554 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/Schema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/Schema.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol OpenAPISchema: Identifiable { static func openAPISchema() -> JSONSchema diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/TextSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/TextSchema.swift index 5e80269..e273786 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/TextSchema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/TextSchema.swift @@ -5,18 +5,18 @@ // Created by Tibor Bodecs on 25/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol TextSchema: Schema { - static var examples: [String] { get } + static var example: String? { get } } public extension TextSchema { - static func openAPISchema() -> OpenAPIKit.JSONSchema { + static func openAPISchema() -> JSONSchema { .text( description: description, - examples: examples.map { .init(stringLiteral: $0) } + example: example.map { .init(stringLiteral: $0) } ) } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/UUIDSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/UUIDSchema.swift index ef1a19a..83f4d13 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/UUIDSchema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/UUIDSchema.swift @@ -5,30 +5,24 @@ // Created by Tibor Bodecs on 25/01/2024. // -import OpenAPIKit +import OpenAPIKit30 import Foundation public protocol UUIDSchema: Schema { - static var examples: [UUID] { get } + static var example: UUID? { get } } public extension UUIDSchema { - static var examples: [UUID] { - [ - .init(uuidString: "F257448D-73F6-4D6F-BB8A-8D756A622F70")!, - .init(uuidString: "84FB7C06-838A-4712-9E2A-C76294670C04")!, - .init(uuidString: "BA4D8D41-BF9F-4122-8546-AB82A98BE28F")!, - .init(uuidString: "519749BB-1210-4479-9B9E-292FC76444C7")!, - .init(uuidString: "E0A42968-28EE-4D1F-BE18-13D526458686")!, - ] + static var example: UUID? { + .init(uuidString: "F257448D-73F6-4D6F-BB8A-8D756A622F70")! } static func openAPISchema() -> JSONSchema { .string( - format: .uuid, + format: .generic, description: description, - examples: examples.map { .init($0.uuidString) } + example: example.map { .init($0.uuidString) } ) } } diff --git a/Sources/FeatherOpenAPI/Interfaces/SecurityScheme/SecurityScheme.swift b/Sources/FeatherOpenAPI/Interfaces/SecurityScheme/SecurityScheme.swift index 4f073bc..fe5f24f 100644 --- a/Sources/FeatherOpenAPI/Interfaces/SecurityScheme/SecurityScheme.swift +++ b/Sources/FeatherOpenAPI/Interfaces/SecurityScheme/SecurityScheme.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol OpenAPISecurityScheme: Identifiable { static func openAPISecurityScheme() -> OpenAPI.SecurityScheme diff --git a/Sources/FeatherOpenAPI/Interfaces/Tag/Tag.swift b/Sources/FeatherOpenAPI/Interfaces/Tag/Tag.swift index 9ba2b03..1b62dc7 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Tag/Tag.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Tag/Tag.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import OpenAPIKit +import OpenAPIKit30 public protocol OpenAPITag: Identifiable { static func openAPITag() -> OpenAPI.Tag diff --git a/Sources/FeatherOpenAPI30/Callback.swift b/Sources/FeatherOpenAPI30/Callback.swift index 92c7ada..9b1b5cc 100644 --- a/Sources/FeatherOpenAPI30/Callback.swift +++ b/Sources/FeatherOpenAPI30/Callback.swift @@ -26,13 +26,5 @@ public struct Callback: CallbackRepresentable { public func openAPICallback() -> Callback { fatalError() - // .init( - // operationId: <#T##String#>, - // parameters: <#T##OrderedDictionary>#>, - // requestBody: <#T##Either?#>, - // description: <#T##String?#>, - // server: <#T##OpenAPI.Server?#>, - // vendorExtensions: <#T##[String : AnyCodable]#> - // ) } } diff --git a/Sources/FeatherOpenAPI30/ComponentBuilder.swift b/Sources/FeatherOpenAPI30/ComponentBuilder.swift index 3043d5d..3e198d8 100644 --- a/Sources/FeatherOpenAPI30/ComponentBuilder.swift +++ b/Sources/FeatherOpenAPI30/ComponentBuilder.swift @@ -8,76 +8,74 @@ import OpenAPIKit30 public struct ComponentBuilder { - + public var components: Components - + public init( components: Components = .init() ) { self.components = components } - + public mutating func header( id: String, builder: (() -> HeaderRepresentable) ) -> (id: HeaderID, value: HeaderRepresentable) { let header = builder() - + components.headers[.init(id)] = header - + return (id: .init(id), value: header) } - + public mutating func parameter( id: String, - builder: (() -> ParameterRepresentable) - ) -> (id: ParameterID, value: ParameterRepresentable) { + builder: (() -> OpenAPI.Parameter) + ) -> (id: ParameterID, value: OpenAPI.Parameter) { let parameter = builder() - + components.parameters[.init(id)] = parameter - + return (id: .init(id), value: parameter) } - + public mutating func schema( id: String, builder: (() -> SchemaRepresentable) ) -> (id: SchemaID, value: SchemaRepresentable) { let schema = builder() - + if components.schemas[.init(id)] != nil { print("Schema `\(id)` is already registered...") } components.schemas[.init(id)] = schema - + return (id: .init(id), value: schema) } - + public mutating func response( id: String, builder: (() -> ResponseRepresentable) ) -> (id: ResponseID, value: ResponseRepresentable) { let response = builder() - + components.responses[.init(id)] = response - + return (id: .init(id), value: response) } - - + public mutating func requestBody( id: String, builder: (() -> RequestBodyRepresentable) ) -> (id: RequestBodyID, value: RequestBodyRepresentable) { let requestBody = builder() - + components.requestBodies[.init(id)] = requestBody - + return (id: .init(id), value: requestBody) } - - + public mutating func addSchema( id: String, builder: (() -> SchemaRepresentable) @@ -85,31 +83,31 @@ public struct ComponentBuilder { let schema = builder() components.schemas[.init(id)] = schema - + return (id: .init(id), value: schema) } - -// mutating func getSchema( -// id: String -// ) throws(ComponentBuilderError) -> (id: SchemaID, value: SchemaRepresentable) { -// guard let schema = components.schemas[.init(id)] else { -// throw .missingSchema(id) -// } -// return (id: .init(id), value: schema) -// } -// -// mutating func getSchemaID( -// id: String -// ) throws(ComponentBuilderError) -> SchemaID { -// guard let schema = components.schemas[.init(id)] else { -// throw .missingSchema(id) -// } -// return (id: .init(id), value: schema) -// } -} + public mutating func getSchema( + id: String + ) throws(ComponentBuilderError) -> ( + id: SchemaID, value: SchemaRepresentable + ) { + guard let schema = components.schemas[.init(id)] else { + throw .missingSchema(id) + } + return (id: .init(id), value: schema) + } + public mutating func getSchemaID( + _ id: String + ) throws(ComponentBuilderError) -> SchemaID { + guard components.schemas[.init(id)] != nil else { + throw .missingSchema(id) + } + return .init(id) + } +} -//public enum ComponentBuilderError: Error { -// case missingSchema(String) -//} +public enum ComponentBuilderError: Error { + case missingSchema(String) +} diff --git a/Sources/FeatherOpenAPI30/Components.swift b/Sources/FeatherOpenAPI30/Components.swift index f4b60be..e79ee5a 100644 --- a/Sources/FeatherOpenAPI30/Components.swift +++ b/Sources/FeatherOpenAPI30/Components.swift @@ -7,6 +7,11 @@ import OpenAPIKit30 +extension JSONSchema: SchemaRepresentable { + + public func openAPISchema() -> JSONSchema { self } +} + public protocol ComponentsRepresentable { func openAPIComponents() -> OpenAPI.Components } @@ -14,24 +19,30 @@ public protocol ComponentsRepresentable { public struct Components: ComponentsRepresentable { public var schemas: OrderedDictionary - public var parameters: OrderedDictionary + public var parameters: OrderedDictionary public var examples: OrderedDictionary public var responses: OrderedDictionary - public var requestBodies: OrderedDictionary + public var requestBodies: + OrderedDictionary public var headers: OrderedDictionary - public var securitySchemes: OrderedDictionary + public var securitySchemes: + OrderedDictionary public var links: OrderedDictionary // public var callbacks: OrderedDictionary public var vendorExtensions: [String: AnyCodable] public init( schemas: OrderedDictionary = [:], - parameters: OrderedDictionary = [:], + parameters: OrderedDictionary = [:], examples: OrderedDictionary = [:], responses: OrderedDictionary = [:], - requestBodies: OrderedDictionary = [:], + requestBodies: OrderedDictionary< + RequestBodyID, RequestBodyRepresentable + > = [:], headers: OrderedDictionary = [:], - securitySchemes: OrderedDictionary = [:], + securitySchemes: OrderedDictionary< + SecuritySchemeID, SecuritySchemeRepresentable + > = [:], links: OrderedDictionary = [:], // callbacks: OrderedDictionary = [:], vendorExtensions: [String: AnyCodable] = [:] @@ -48,9 +59,7 @@ public struct Components: ComponentsRepresentable { self.vendorExtensions = vendorExtensions } - func openAPISchemas( - - ) -> OpenAPI.ComponentDictionary { + func openAPISchemas() -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in schemas { @@ -59,9 +68,7 @@ public struct Components: ComponentsRepresentable { return result } - func openAPIResponses( - - ) -> OpenAPI.ComponentDictionary { + func openAPIResponses() -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in responses { @@ -70,21 +77,16 @@ public struct Components: ComponentsRepresentable { return result } - func openAPIParameters( - - ) -> OpenAPI.ComponentDictionary { + func openAPIParameters() -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in parameters { - result[.init(stringLiteral: key.rawValue)] = - value.openAPIParameter() + result[.init(stringLiteral: key.rawValue)] = value } return result } - func openAPIExamples( - - ) -> OpenAPI.ComponentDictionary { + func openAPIExamples() -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in examples { @@ -93,9 +95,8 @@ public struct Components: ComponentsRepresentable { return result } - func openAPIRequestBodies( - - ) -> OpenAPI.ComponentDictionary { + func openAPIRequestBodies() -> OpenAPI.ComponentDictionary + { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in requestBodies { @@ -105,9 +106,7 @@ public struct Components: ComponentsRepresentable { return result } - func openAPIHeaders( - - ) -> OpenAPI.ComponentDictionary { + func openAPIHeaders() -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in headers { @@ -116,9 +115,9 @@ public struct Components: ComponentsRepresentable { return result } - func openAPISecuritySchemes( - - ) -> OpenAPI.ComponentDictionary { + func openAPISecuritySchemes() + -> OpenAPI.ComponentDictionary + { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in securitySchemes { @@ -128,9 +127,7 @@ public struct Components: ComponentsRepresentable { return result } - func openAPILinks( - - ) -> OpenAPI.ComponentDictionary { + func openAPILinks() -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in links { @@ -162,7 +159,5 @@ public struct Components: ComponentsRepresentable { vendorExtensions: vendorExtensions ) } - - -} +} diff --git a/Sources/FeatherOpenAPI30/Schema.swift b/Sources/FeatherOpenAPI30/Schema.swift index eac514a..e761316 100644 --- a/Sources/FeatherOpenAPI30/Schema.swift +++ b/Sources/FeatherOpenAPI30/Schema.swift @@ -97,7 +97,7 @@ public struct StringSchema: SchemaRepresentable { } public struct ObjectSchema: SchemaRepresentable { - + public var format: JSONTypeFormat.ObjectFormat public var required: Bool public var nullable: Bool? @@ -110,12 +110,12 @@ public struct ObjectSchema: SchemaRepresentable { public var minProperties: Int? public var maxProperties: Int? public var properties: OrderedDictionary -// public var properties: String? -// public var additionalProperties: String? + // public var properties: String? + // public var additionalProperties: String? public var allowedValues: [AnyCodable]? public var defaultValue: AnyCodable? public var example: AnyCodable? - + public init( format: JSONTypeFormat.ObjectFormat = .generic, required: Bool = false, @@ -146,12 +146,12 @@ public struct ObjectSchema: SchemaRepresentable { self.minProperties = minProperties self.maxProperties = maxProperties self.properties = properties -// self.pattern = pattern + // self.pattern = pattern self.allowedValues = allowedValues self.defaultValue = defaultValue self.example = example } - + public func openAPISchema() -> JSONSchema { .object( format: format, @@ -166,9 +166,9 @@ public struct ObjectSchema: SchemaRepresentable { minProperties: minProperties, maxProperties: maxProperties, properties: properties.mapValues { $0.openAPISchema() }, -// properties: [ -// "asf": .reference(.component(named: "")), -// ], + // properties: [ + // "asf": .reference(.component(named: "")), + // ], additionalProperties: nil, allowedValues: allowedValues, defaultValue: defaultValue, diff --git a/Tests/FeatherOpenAPI30Tests/Example/CreateExampleOperation.swift b/Tests/FeatherOpenAPI30Tests/Example/CreateExampleOperation.swift index 1359d65..4f16437 100644 --- a/Tests/FeatherOpenAPI30Tests/Example/CreateExampleOperation.swift +++ b/Tests/FeatherOpenAPI30Tests/Example/CreateExampleOperation.swift @@ -8,6 +8,29 @@ import FeatherOpenAPI30 import OpenAPIKit30 +extension ComponentBuilder { + + mutating func todoTitle() -> SchemaID { + let sch1 = schema(id: #function) { + StringSchema() + } + return sch1.id + } +} + +enum Model { + struct PetSchemaBuilder { + + var builder: ComponentBuilder + + mutating func title() -> SchemaID { + let sch1 = builder.schema(id: #function) { + StringSchema() + } + return sch1.id + } + } +} func createExample( using builder: inout ComponentBuilder @@ -16,23 +39,19 @@ func createExample( let sch1 = builder.schema(id: "TestSchema") { StringSchema() } - -// let sch2 = builder.schema(id: "todo.id") { -// StringSchema() -// } - + + // let sch2 = builder.schema(id: "todo.id") { + // StringSchema() + // } + let header = builder.header(id: "header") { Header(schema: sch1.id) } - + let p1 = builder.parameter(id: "foo") { - Parameter( - name: "foo", - context: .path, - schema: sch1.id - ) + .init(name: "foo", context: .path, schema: .string) } - + let reqBody1 = builder.requestBody(id: "foo") { RequestBody( description: "This is a proper request body", @@ -59,7 +78,7 @@ func createExample( summary: "Detail example", description: "Detail example detail", parameters: [ - p1.id, + p1.id ], requestBody: reqBody1.id, responses: [ @@ -67,4 +86,3 @@ func createExample( ] ) } - diff --git a/Tests/FeatherOpenAPI30Tests/Example/GetExampleOperation.swift b/Tests/FeatherOpenAPI30Tests/Example/GetExampleOperation.swift index 682f8d8..e1ae64b 100644 --- a/Tests/FeatherOpenAPI30Tests/Example/GetExampleOperation.swift +++ b/Tests/FeatherOpenAPI30Tests/Example/GetExampleOperation.swift @@ -8,44 +8,60 @@ import FeatherOpenAPI30 import OpenAPIKit30 +extension SchemaID { + static var exampleID: Self { .init(#function) } +} -func getExample( +func registerSchemes( using builder: inout ComponentBuilder -) -> Operation { - +) { let idSchema = builder.schema(id: "ExampleId") { StringSchema() } - +} + +func getExample( + using builder: inout ComponentBuilder +) -> Operation { + + registerSchemes(using: &builder) + + let exampleId = try! builder.getSchemaID("ExampleId") + let titleSchema = builder.schema(id: "ExampleTitle") { StringSchema() } let detailSchema = builder.schema(id: "ExampleDetail") { - ObjectSchema( + return JSONSchema.object( properties: [ - "id": StringSchema(), - "title": StringSchema(), + "id": .reference(.component(named: exampleId.rawValue)), + "title": .reference(.component(named: titleSchema.id.rawValue)), ] ) + // ObjectSchema( + // properties: [ + // "id": StringSchema(), + // "title": StringSchema(), + // ] + // ) } - - - + let idParameter = builder.parameter(id: "ExampleId") { Parameter( name: "id", context: .path, - schema: idSchema.id + schema: exampleId ) + .openAPIParameter() } - + let okResponse = builder.response(id: "GetExampleResponse") { - Response( + return Response( description: "Example detail response", -// headers: [ -// "X-Custom-Response-Header": header.id -// ], + // headers: [ + // "X-Custom-Response-Header": header.id + // ], content: [ .aac: Content(schema: detailSchema.id) ] @@ -57,11 +73,10 @@ func getExample( summary: "Detail example", description: "Detail example detail", parameters: [ - idParameter.id, + idParameter.id ], responses: [ 200: okResponse.id ] ) } - diff --git a/Tests/FeatherOpenAPI30Tests/Example/TestOperation.swift b/Tests/FeatherOpenAPI30Tests/Example/TestOperation.swift index 47acc0f..d1d89ab 100644 --- a/Tests/FeatherOpenAPI30Tests/Example/TestOperation.swift +++ b/Tests/FeatherOpenAPI30Tests/Example/TestOperation.swift @@ -8,7 +8,6 @@ import FeatherOpenAPI30 import OpenAPIKit30 - func testOperation( using builder: inout ComponentBuilder ) -> Operation { @@ -16,23 +15,24 @@ func testOperation( let sch1 = builder.schema(id: "TestSchema") { StringSchema() } - -// let sch2 = builder.schema(id: "todo.id") { -// StringSchema() -// } - + + // let sch2 = builder.schema(id: "todo.id") { + // StringSchema() + // } + let header = builder.header(id: "header") { Header(schema: sch1.id) } - + let p1 = builder.parameter(id: "foo") { Parameter( name: "foo", context: .path, schema: sch1.id ) + .openAPIParameter() } - + let reqBody1 = builder.requestBody(id: "foo") { RequestBody( description: "This is a proper request body", @@ -56,7 +56,7 @@ func testOperation( return .init( parameters: [ - p1.id, + p1.id ], requestBody: reqBody1.id, responses: [ @@ -64,4 +64,3 @@ func testOperation( ] ) } - diff --git a/Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift index 70762a8..265b47f 100644 --- a/Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift +++ b/Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift @@ -17,13 +17,33 @@ import Testing @Suite struct FeatherOpenAPIKitTests { + @Test + func ref() throws { + + struct UserId: SchemaRepresentable { + + func openAPISchema() -> OpenAPIKit30.JSONSchema { + fatalError() + } + } + + struct Reference { + + static func toSchemaRef() { + print(T.self) + } + } + + Reference.self.toSchemaRef() + } + @Test func example() throws { - + var builder = ComponentBuilder() - + let getExampleOperation = getExample(using: &builder) -// let createExampleOperation = createExample(using: &builder) + // let createExampleOperation = createExample(using: &builder) let doc = Document( info: Info( @@ -34,12 +54,12 @@ struct FeatherOpenAPIKitTests { "examples": PathItem( summary: "Example related operations", get: getExampleOperation, -// post: createExampleOperation + // post: createExampleOperation ) ], components: builder.components ) - + let openAPIdoc = doc.openAPIDocument() let encoder = YAMLEncoder() @@ -57,9 +77,8 @@ struct FeatherOpenAPIKitTests { let result = try encoder.encode(openAPIdoc) print(result) - - } + } func renderTest() throws { @@ -89,7 +108,7 @@ struct FeatherOpenAPIKitTests { "schemaID": .string( format: .dateTime, example: "Foo" - ), + ) ], requestBodies: [ "foo": .init( diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift index db3ee62..1c478d8 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift @@ -19,7 +19,6 @@ extension Example.Model { enum Operations { enum Get: Operation { - static let ffff: String = "asdf" static var tag: Tag.Type { Tags.Main.self } static let summary = "Detail example" static let description = "Detail example detail" diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Responses.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Responses.swift index 6368d0c..c6d4fc6 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Responses.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Responses.swift @@ -6,7 +6,7 @@ // import FeatherOpenAPI -import OpenAPIKit +import OpenAPIKit30 extension Example.Model { diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Schemas.swift index 1316820..083f584 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Schemas.swift @@ -31,16 +31,12 @@ extension Example.Model { enum CustomHeader: TextSchema { static let description = "Custom header" - static let examples = [ - "my-example-key" - ] + static let example: String? = "my-example-key" } enum Key: TextSchema { static let description = "Key of the example model" - static let examples = [ - "my-example-key" - ] + static let example: String? = "my-example-key" } enum Create: ObjectSchema { diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift index a5a8810..7550d3e 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift @@ -6,7 +6,7 @@ // import FeatherOpenAPI -import OpenAPIKit +import OpenAPIKit30 // shared operation security extension Operation { diff --git a/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift index 2c37e58..09db0ae 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift @@ -25,18 +25,15 @@ extension ExampleDuplicatedItem.Model { enum Key: TextSchema { static let description = "Key of the example model" - static let examples = [ - "my-example-key" - ] + static let example: String? = "my-example-key" + } enum KeySecond: TextSchema { static let id = Key.id static let description = "Key of the example model" - static let examples = [ - "my-example-key" - ] + static let example: String? = "my-example-key" } } } diff --git a/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift index 04ab2c2..041d904 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift @@ -25,9 +25,7 @@ extension ExampleMissingParentItem.Model { enum Key: TextSchema { static let override = true static let description = "Key of the example model" - static let examples = [ - "my-example-key" - ] + static let example: String? = "my-example-key" } } diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift index f17e910..c27e306 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift @@ -6,7 +6,7 @@ // import FeatherOpenAPI -import OpenAPIKit +import OpenAPIKit30 import Foundation struct ExampleDocument: Document { diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift index 64a7137..191039d 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift @@ -6,7 +6,7 @@ // import FeatherOpenAPI -import OpenAPIKit +import OpenAPIKit30 import Foundation struct ExampleDuplicatedItemDocument: Document { diff --git a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift index 863873f..c1ea820 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift @@ -6,7 +6,7 @@ // import FeatherOpenAPI -import OpenAPIKit +import OpenAPIKit30 import Foundation struct ExampleMissingParentItemItemDocument: Document { diff --git a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift index 6a5fccb..bdf7602 100644 --- a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift +++ b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift @@ -6,7 +6,7 @@ // import Foundation -import OpenAPIKit +import OpenAPIKit30 import OpenAPIKitCore import Yams import Testing From 57d3ac42de83b33c56f72cd5f33a0ff8d10fc3c2 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Thu, 22 Jan 2026 14:05:01 +0100 Subject: [PATCH 11/34] downgrade old framework to openapikit30 --- Package.swift | 16 +++- .../Extensions/JSONSchema+Enumeration.swift | 2 +- .../Extensions/JSONSchema+Text.swift | 2 +- .../Interfaces/Header/Header.swift | 9 --- .../Interfaces/Identifiable.swift | 2 +- .../Interfaces/Operation/Operation.swift | 1 - .../Interfaces/Parameter/Parameter.swift | 10 --- .../Interfaces/Response/Response.swift | 10 --- .../Interfaces/Schema/BooleanSchema.swift | 8 +- .../Interfaces/Schema/DateTimeSchema.swift | 6 +- .../Interfaces/Schema/Schema.swift | 10 +-- .../Interfaces/Schema/TextSchema.swift | 4 + .../FeatherOpenAPI30/ComponentBuilder.swift | 73 +++++-------------- .../Example/CreateExampleOperation.swift | 50 +++---------- .../Example/GetExampleOperation.swift | 32 +++----- .../Example/TestOperation.swift | 24 +++--- .../Petstore/Petstore.swift | 56 ++++++++++++++ .../ApiResponse/ApiResponse+Schemas.swift | 37 ++++++++++ .../Petstore/ApiResponse/ApiResponse.swift | 14 ++++ .../Petstore/Category/Category+Schemas.swift | 33 +++++++++ .../Petstore/Category/Category.swift | 13 ++++ .../Petstore/Pet/Pet+Operations.swift | 32 ++++++++ .../Petstore/Pet/Pet+Parameters.swift | 21 ++++++ .../Petstore/Pet/Pet+PathItems.swift | 32 ++++++++ .../Petstore/Pet/Pet+Responses.swift | 32 ++++++++ .../Petstore/Pet/Pet+Schemas.swift | 67 +++++++++++++++++ .../Petstore/Pet/Pet+Tags.swift | 18 +++++ .../Petstore/Pet/Pet.swift | 13 ++++ .../Petstore/Petstore.swift | 61 ++++++++++++++++ .../Petstore/Store/Store+Schemas.swift | 61 ++++++++++++++++ .../Petstore/Store/Store.swift | 13 ++++ .../Petstore/Tag/Tag+Schemas.swift | 31 ++++++++ .../Petstore/Tag/Tag.swift | 13 ++++ .../Petstore/User/User+Schemas.swift | 63 ++++++++++++++++ .../Petstore/User/User.swift | 13 ++++ .../PetstoreOpenAPIKitTests.swift | 42 +++++++++++ .../Model/ExampleModel+Operations.swift | 6 +- .../Example/Model/ExampleModel.swift | 21 +++++- .../Model/ExampleDuplicatedItemModel.swift | 14 +++- .../Model/ExampleMissingParentItemModel.swift | 13 +++- .../FeatherOpenAPIKitTests.swift | 24 ++---- 41 files changed, 801 insertions(+), 201 deletions(-) create mode 100644 Tests/FeatherOpenAPI30Tests/Petstore/Petstore.swift create mode 100644 Tests/FeatherOpenAPIPluginTests/Petstore/ApiResponse/ApiResponse+Schemas.swift create mode 100644 Tests/FeatherOpenAPIPluginTests/Petstore/ApiResponse/ApiResponse.swift create mode 100644 Tests/FeatherOpenAPIPluginTests/Petstore/Category/Category+Schemas.swift create mode 100644 Tests/FeatherOpenAPIPluginTests/Petstore/Category/Category.swift create mode 100644 Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Operations.swift create mode 100644 Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Parameters.swift create mode 100644 Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+PathItems.swift create mode 100644 Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Responses.swift create mode 100644 Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Schemas.swift create mode 100644 Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Tags.swift create mode 100644 Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet.swift create mode 100644 Tests/FeatherOpenAPIPluginTests/Petstore/Petstore.swift create mode 100644 Tests/FeatherOpenAPIPluginTests/Petstore/Store/Store+Schemas.swift create mode 100644 Tests/FeatherOpenAPIPluginTests/Petstore/Store/Store.swift create mode 100644 Tests/FeatherOpenAPIPluginTests/Petstore/Tag/Tag+Schemas.swift create mode 100644 Tests/FeatherOpenAPIPluginTests/Petstore/Tag/Tag.swift create mode 100644 Tests/FeatherOpenAPIPluginTests/Petstore/User/User+Schemas.swift create mode 100644 Tests/FeatherOpenAPIPluginTests/Petstore/User/User.swift create mode 100644 Tests/FeatherOpenAPIPluginTests/PetstoreOpenAPIKitTests.swift diff --git a/Package.swift b/Package.swift index 034dfb2..0430163 100644 --- a/Package.swift +++ b/Package.swift @@ -68,10 +68,7 @@ let package = Package( .product(name: "OpenAPIKitCompat", package: "OpenAPIKit"), .product(name: "Yams", package: "yams"), ], - swiftSettings: defaultSwiftSettings, - plugins: [ - .plugin(name: "FeatherOpenAPIGenerator") - ], + swiftSettings: defaultSwiftSettings ), .target( name: "FeatherOpenAPI30", @@ -96,6 +93,17 @@ let package = Package( ], swiftSettings: defaultSwiftSettings, ), + .testTarget( + name: "FeatherOpenAPIPluginTests", + dependencies: [ + .product(name: "Yams", package: "Yams"), + .target(name: "FeatherOpenAPI"), + ], + swiftSettings: defaultSwiftSettings, + plugins: [ + .plugin(name: "FeatherOpenAPIGenerator"), + ] + ), .testTarget( name: "FeatherOpenAPI30Tests", dependencies: [ diff --git a/Sources/FeatherOpenAPI/Extensions/JSONSchema+Enumeration.swift b/Sources/FeatherOpenAPI/Extensions/JSONSchema+Enumeration.swift index ef976a5..b1753fb 100644 --- a/Sources/FeatherOpenAPI/Extensions/JSONSchema+Enumeration.swift +++ b/Sources/FeatherOpenAPI/Extensions/JSONSchema+Enumeration.swift @@ -10,7 +10,7 @@ import OpenAPIKit30 public extension JSONSchema { static func enumeration( - description: String, + description: String?, allowedValues: [AnyCodable], defaultValue: AnyCodable? = nil, example: AnyCodable? = nil diff --git a/Sources/FeatherOpenAPI/Extensions/JSONSchema+Text.swift b/Sources/FeatherOpenAPI/Extensions/JSONSchema+Text.swift index b165093..0a63682 100644 --- a/Sources/FeatherOpenAPI/Extensions/JSONSchema+Text.swift +++ b/Sources/FeatherOpenAPI/Extensions/JSONSchema+Text.swift @@ -10,7 +10,7 @@ import OpenAPIKit30 public extension JSONSchema { static func text( - description: String, + description: String?, example: AnyCodable? = nil ) -> Self { .string( diff --git a/Sources/FeatherOpenAPI/Interfaces/Header/Header.swift b/Sources/FeatherOpenAPI/Interfaces/Header/Header.swift index 4cee726..88038c2 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Header/Header.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Header/Header.swift @@ -17,15 +17,6 @@ public protocol Header: OpenAPIHeader { static var schema: Schema.Type { get } } -public extension Header { - - // static func reference() -> Either< - // OpenAPI.Reference, OpenAPI.Header - // > { - // .reference(.component(named: id)) - // } -} - public extension Header { static var id: String { name } diff --git a/Sources/FeatherOpenAPI/Interfaces/Identifiable.swift b/Sources/FeatherOpenAPI/Interfaces/Identifiable.swift index 1fb4f25..5e178f3 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Identifiable.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Identifiable.swift @@ -7,7 +7,7 @@ import OpenAPIKit30 -public protocol Identifiable { +public protocol Identifiable: Sendable { static var id: String { get } static var override: Bool { get } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Operation/Operation.swift b/Sources/FeatherOpenAPI/Interfaces/Operation/Operation.swift index 5d64659..ae76bd5 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Operation/Operation.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Operation/Operation.swift @@ -54,7 +54,6 @@ public extension Operation { $0[.init(integerLiteral: $1.statusCode)] = .reference( .component(named: $1.response.id) ) - // .reference() }, security: security.map { [$0.reference(): []] } ) diff --git a/Sources/FeatherOpenAPI/Interfaces/Parameter/Parameter.swift b/Sources/FeatherOpenAPI/Interfaces/Parameter/Parameter.swift index 8676557..50302eb 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Parameter/Parameter.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Parameter/Parameter.swift @@ -11,16 +11,6 @@ public protocol OpenAPIParameter: Identifiable { static func openAPIParameter() -> OpenAPI.Parameter } -public extension OpenAPIParameter { - - // static func reference() -> Either< - // OpenAPI.Reference, - // OpenAPI.Parameter - // > { - // .reference(.component(named: id)) - // } -} - public protocol Parameter: OpenAPIParameter { static var name: String { get } static var context: OpenAPI.Parameter.Context { get } diff --git a/Sources/FeatherOpenAPI/Interfaces/Response/Response.swift b/Sources/FeatherOpenAPI/Interfaces/Response/Response.swift index b981b47..b9eb3d7 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Response/Response.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Response/Response.swift @@ -11,16 +11,6 @@ public protocol OpenAPIResponse: Identifiable { static func openAPIResponse() -> OpenAPI.Response } -public extension OpenAPIResponse { - - // static func reference() -> Either< - // OpenAPI.Reference, - // OpenAPI.Response - // > { - // .reference(.component(named: id)) - // } -} - public protocol Response: OpenAPIResponse { static var description: String { get } static var headers: [Header.Type] { get } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/BooleanSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/BooleanSchema.swift index 7305d47..de68b99 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/BooleanSchema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/BooleanSchema.swift @@ -1,14 +1,18 @@ import OpenAPIKit30 public protocol BooleanSchema: Schema { - static var defaultValue: Bool { get } + static var defaultValue: Bool? { get } +} + +public extension BooleanSchema { + static var defaultValue: Bool? { nil } } extension BooleanSchema { public static func openAPISchema() -> JSONSchema { .boolean( description: description, - defaultValue: .init(defaultValue) + defaultValue: defaultValue.map { .init($0) } ) } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/DateTimeSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/DateTimeSchema.swift index 3112837..d1ea3f1 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/DateTimeSchema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/DateTimeSchema.swift @@ -1,17 +1,17 @@ import OpenAPIKit30 public protocol DateTimeSchema: Schema { - static var example: String { get } + static var example: String? { get } } extension DateTimeSchema { - public static var examples: [String] { ["2023-04-04T09:20:15.000Z"] } + public static var example: String? { "2026-01-2T09:20:15.000Z" } public static func openAPISchema() -> JSONSchema { .string( format: .dateTime, description: description, - example: .init(stringLiteral: example) + example: example.map { .init(stringLiteral: $0) } ) } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/Schema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/Schema.swift index 957a554..79ab437 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/Schema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/Schema.swift @@ -23,16 +23,10 @@ public extension OpenAPISchema { } public protocol Schema: OpenAPISchema { - static var description: String { get } + static var description: String? { get } } extension Schema { - public static var description: String { - let desc = String(describing: self) - if desc.hasSuffix("Schema") { - return desc.dropLast("Schema".count) + " description" - } - return desc + " description" - } + public static var description: String? { nil } } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/TextSchema.swift b/Sources/FeatherOpenAPI/Interfaces/Schema/TextSchema.swift index e273786..3d129aa 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Schema/TextSchema.swift +++ b/Sources/FeatherOpenAPI/Interfaces/Schema/TextSchema.swift @@ -11,6 +11,10 @@ public protocol TextSchema: Schema { static var example: String? { get } } +public extension TextSchema { + static var example: String? { nil } +} + public extension TextSchema { static func openAPISchema() -> JSONSchema { diff --git a/Sources/FeatherOpenAPI30/ComponentBuilder.swift b/Sources/FeatherOpenAPI30/ComponentBuilder.swift index 3e198d8..431f0c1 100644 --- a/Sources/FeatherOpenAPI30/ComponentBuilder.swift +++ b/Sources/FeatherOpenAPI30/ComponentBuilder.swift @@ -20,94 +20,61 @@ public struct ComponentBuilder { public mutating func header( id: String, builder: (() -> HeaderRepresentable) - ) -> (id: HeaderID, value: HeaderRepresentable) { + ) -> HeaderID { + let id = HeaderID(id) let header = builder() - components.headers[.init(id)] = header + components.headers[id] = header - return (id: .init(id), value: header) + return id } public mutating func parameter( id: String, builder: (() -> OpenAPI.Parameter) - ) -> (id: ParameterID, value: OpenAPI.Parameter) { + ) -> ParameterID { + let id = ParameterID(id) let parameter = builder() - components.parameters[.init(id)] = parameter + components.parameters[id] = parameter - return (id: .init(id), value: parameter) + return id } public mutating func schema( id: String, builder: (() -> SchemaRepresentable) - ) -> (id: SchemaID, value: SchemaRepresentable) { + ) -> SchemaID { + let id = SchemaID(id) let schema = builder() - if components.schemas[.init(id)] != nil { - print("Schema `\(id)` is already registered...") - } + components.schemas[id] = schema - components.schemas[.init(id)] = schema - - return (id: .init(id), value: schema) + return id } public mutating func response( id: String, builder: (() -> ResponseRepresentable) - ) -> (id: ResponseID, value: ResponseRepresentable) { + ) -> ResponseID { + let id = ResponseID(id) let response = builder() - components.responses[.init(id)] = response + components.responses[id] = response - return (id: .init(id), value: response) + return id } public mutating func requestBody( id: String, builder: (() -> RequestBodyRepresentable) - ) -> (id: RequestBodyID, value: RequestBodyRepresentable) { + ) -> RequestBodyID { + let id = RequestBodyID(id) let requestBody = builder() - components.requestBodies[.init(id)] = requestBody - - return (id: .init(id), value: requestBody) - } - - public mutating func addSchema( - id: String, - builder: (() -> SchemaRepresentable) - ) -> (id: SchemaID, value: SchemaRepresentable) { - let schema = builder() - - components.schemas[.init(id)] = schema - - return (id: .init(id), value: schema) - } + components.requestBodies[id] = requestBody - public mutating func getSchema( - id: String - ) throws(ComponentBuilderError) -> ( - id: SchemaID, value: SchemaRepresentable - ) { - guard let schema = components.schemas[.init(id)] else { - throw .missingSchema(id) - } - return (id: .init(id), value: schema) + return id } - public mutating func getSchemaID( - _ id: String - ) throws(ComponentBuilderError) -> SchemaID { - guard components.schemas[.init(id)] != nil else { - throw .missingSchema(id) - } - return .init(id) - } -} - -public enum ComponentBuilderError: Error { - case missingSchema(String) } diff --git a/Tests/FeatherOpenAPI30Tests/Example/CreateExampleOperation.swift b/Tests/FeatherOpenAPI30Tests/Example/CreateExampleOperation.swift index 4f16437..699a064 100644 --- a/Tests/FeatherOpenAPI30Tests/Example/CreateExampleOperation.swift +++ b/Tests/FeatherOpenAPI30Tests/Example/CreateExampleOperation.swift @@ -8,67 +8,39 @@ import FeatherOpenAPI30 import OpenAPIKit30 -extension ComponentBuilder { - - mutating func todoTitle() -> SchemaID { - let sch1 = schema(id: #function) { - StringSchema() - } - return sch1.id - } -} - -enum Model { - struct PetSchemaBuilder { - - var builder: ComponentBuilder - - mutating func title() -> SchemaID { - let sch1 = builder.schema(id: #function) { - StringSchema() - } - return sch1.id - } - } -} - func createExample( using builder: inout ComponentBuilder ) -> Operation { - let sch1 = builder.schema(id: "TestSchema") { + let testSchemaID = builder.schema(id: "TestSchema") { StringSchema() } - // let sch2 = builder.schema(id: "todo.id") { - // StringSchema() - // } - - let header = builder.header(id: "header") { - Header(schema: sch1.id) + let headerID = builder.header(id: "header") { + Header(schema: testSchemaID) } let p1 = builder.parameter(id: "foo") { .init(name: "foo", context: .path, schema: .string) } - let reqBody1 = builder.requestBody(id: "foo") { + let requestBodyID = builder.requestBody(id: "foo") { RequestBody( description: "This is a proper request body", content: [ - .json: Content(schema: sch1.id) + .json: Content(schema: testSchemaID) ] ) } - let okResponse = builder.response(id: "foo") { + let okResponseID = builder.response(id: "foo") { Response( description: "foo", headers: [ - "X-Custom-Response-Header": header.id + "X-Custom-Response-Header": headerID ], content: [ - .aac: Content(schema: sch1.id) + .aac: Content(schema: testSchemaID) ] ) } @@ -78,11 +50,11 @@ func createExample( summary: "Detail example", description: "Detail example detail", parameters: [ - p1.id + p1 ], - requestBody: reqBody1.id, + requestBody: requestBodyID, responses: [ - 200: okResponse.id + 200: okResponseID ] ) } diff --git a/Tests/FeatherOpenAPI30Tests/Example/GetExampleOperation.swift b/Tests/FeatherOpenAPI30Tests/Example/GetExampleOperation.swift index e1ae64b..a27c251 100644 --- a/Tests/FeatherOpenAPI30Tests/Example/GetExampleOperation.swift +++ b/Tests/FeatherOpenAPI30Tests/Example/GetExampleOperation.swift @@ -8,35 +8,23 @@ import FeatherOpenAPI30 import OpenAPIKit30 -extension SchemaID { - static var exampleID: Self { .init(#function) } -} - -func registerSchemes( - using builder: inout ComponentBuilder -) { - let idSchema = builder.schema(id: "ExampleId") { - StringSchema() - } -} - func getExample( using builder: inout ComponentBuilder ) -> Operation { - registerSchemes(using: &builder) - - let exampleId = try! builder.getSchemaID("ExampleId") + let exampleID = builder.schema(id: "ExampleId") { + StringSchema() + } - let titleSchema = builder.schema(id: "ExampleTitle") { + let titleSchemaID = builder.schema(id: "ExampleTitle") { StringSchema() } let detailSchema = builder.schema(id: "ExampleDetail") { return JSONSchema.object( properties: [ - "id": .reference(.component(named: exampleId.rawValue)), - "title": .reference(.component(named: titleSchema.id.rawValue)), + "id": .reference(.component(named: exampleID.rawValue)), + "title": .reference(.component(named: titleSchemaID.rawValue)), ] ) // ObjectSchema( @@ -51,7 +39,7 @@ func getExample( Parameter( name: "id", context: .path, - schema: exampleId + schema: exampleID ) .openAPIParameter() } @@ -63,7 +51,7 @@ func getExample( // "X-Custom-Response-Header": header.id // ], content: [ - .aac: Content(schema: detailSchema.id) + .aac: Content(schema: detailSchema) ] ) } @@ -73,10 +61,10 @@ func getExample( summary: "Detail example", description: "Detail example detail", parameters: [ - idParameter.id + idParameter ], responses: [ - 200: okResponse.id + 200: okResponse ] ) } diff --git a/Tests/FeatherOpenAPI30Tests/Example/TestOperation.swift b/Tests/FeatherOpenAPI30Tests/Example/TestOperation.swift index d1d89ab..6b83239 100644 --- a/Tests/FeatherOpenAPI30Tests/Example/TestOperation.swift +++ b/Tests/FeatherOpenAPI30Tests/Example/TestOperation.swift @@ -12,23 +12,19 @@ func testOperation( using builder: inout ComponentBuilder ) -> Operation { - let sch1 = builder.schema(id: "TestSchema") { + let testSchemaID = builder.schema(id: "TestSchema") { StringSchema() } - // let sch2 = builder.schema(id: "todo.id") { - // StringSchema() - // } - - let header = builder.header(id: "header") { - Header(schema: sch1.id) + let headerID = builder.header(id: "header") { + Header(schema: testSchemaID) } let p1 = builder.parameter(id: "foo") { Parameter( name: "foo", context: .path, - schema: sch1.id + schema: testSchemaID ) .openAPIParameter() } @@ -37,7 +33,7 @@ func testOperation( RequestBody( description: "This is a proper request body", content: [ - .json: Content(schema: sch1.id) + .json: Content(schema: testSchemaID) ] ) } @@ -46,21 +42,21 @@ func testOperation( Response( description: "foo", headers: [ - "X-Custom-Response-Header": header.id + "X-Custom-Response-Header": headerID ], content: [ - .aac: Content(schema: sch1.id) + .aac: Content(schema: testSchemaID) ] ) } return .init( parameters: [ - p1.id + p1 ], - requestBody: reqBody1.id, + requestBody: reqBody1, responses: [ - 200: response1.id + 200: response1 ] ) } diff --git a/Tests/FeatherOpenAPI30Tests/Petstore/Petstore.swift b/Tests/FeatherOpenAPI30Tests/Petstore/Petstore.swift new file mode 100644 index 0000000..af20fe2 --- /dev/null +++ b/Tests/FeatherOpenAPI30Tests/Petstore/Petstore.swift @@ -0,0 +1,56 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI30 +import OpenAPIKit30 + +func petstore( + using builder: inout ComponentBuilder +) { + + let apiResponseID = builder.schema(id: "ApiResponse") { + JSONSchema.object( + properties: [ + "code": .integer(format: .int32), + "type": .string, + "message": .string, + ], + ) + } + + let tagID = builder.schema(id: "Tag") { + JSONSchema.object( + properties: [ + "id": .integer(format: .int64), + "name": .string, + ], + ) + } + + let categoryID = builder.schema(id: "Category") { + JSONSchema.object( + properties: [ + "code": .integer(format: .int32), + "type": .string, + "message": .string, + ], + ) + } + + let petID = builder.schema(id: "Pet") { + JSONSchema.object( + properties: [ + "id": .integer(format: .int64, example: 10), + "name": .string(example: "doggie"), + "category": .reference( + .component(named: categoryID.rawValue), + required: true + ), + ], + ) + } +} diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/ApiResponse/ApiResponse+Schemas.swift b/Tests/FeatherOpenAPIPluginTests/Petstore/ApiResponse/ApiResponse+Schemas.swift new file mode 100644 index 0000000..1a00fcb --- /dev/null +++ b/Tests/FeatherOpenAPIPluginTests/Petstore/ApiResponse/ApiResponse+Schemas.swift @@ -0,0 +1,37 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore.ApiResponse { + + enum Schemas { + + enum ApiResponse: ObjectSchema { + + enum Code: Int32Schema { + } + + enum `ResponseType`: TextSchema { + + } + + enum Message: TextSchema { + + } + + static var properties: [ObjectSchemaProperty] { + [ + .init("code", Code.self), + .init("type", `ResponseType`.self), + .init("message", Message.self), + ] + } + } + + } +} diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/ApiResponse/ApiResponse.swift b/Tests/FeatherOpenAPIPluginTests/Petstore/ApiResponse/ApiResponse.swift new file mode 100644 index 0000000..caaf61f --- /dev/null +++ b/Tests/FeatherOpenAPIPluginTests/Petstore/ApiResponse/ApiResponse.swift @@ -0,0 +1,14 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore { + + enum ApiResponse: Component { + } +} diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Category/Category+Schemas.swift b/Tests/FeatherOpenAPIPluginTests/Petstore/Category/Category+Schemas.swift new file mode 100644 index 0000000..8ab2ffc --- /dev/null +++ b/Tests/FeatherOpenAPIPluginTests/Petstore/Category/Category+Schemas.swift @@ -0,0 +1,33 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore.Category { + + enum Schemas { + + enum Category: ObjectSchema { + + enum Id: Int64Schema { + static var example: Int? { 1 } + } + + enum Name: TextSchema { + static var example: String? { "Dogs" } + } + + static var properties: [ObjectSchemaProperty] { + [ + .init("id", Id.self), + .init("name", Name.self), + ] + } + + } + } +} diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Category/Category.swift b/Tests/FeatherOpenAPIPluginTests/Petstore/Category/Category.swift new file mode 100644 index 0000000..5da49e5 --- /dev/null +++ b/Tests/FeatherOpenAPIPluginTests/Petstore/Category/Category.swift @@ -0,0 +1,13 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore { + + enum Category: Component {} +} diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Operations.swift b/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Operations.swift new file mode 100644 index 0000000..8f34d17 --- /dev/null +++ b/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Operations.swift @@ -0,0 +1,32 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore.Pet { + + enum Operations { + + enum Get: Operation { + static var tag: Tag.Type { Tags.Pet.self } + static let summary = "Update an existing pet." + static let description = "Update an existing pet by Id." + static var operationId: String { "updatePet" } + static var parameters: [Parameter.Type] { + [ + Parameters.Id.self + ] + } + static var responses: [OperationResponse] { + [ + .init(200, Responses.Detail.self) + ] + } + } + + } +} diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Parameters.swift b/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Parameters.swift new file mode 100644 index 0000000..4d9fe49 --- /dev/null +++ b/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Parameters.swift @@ -0,0 +1,21 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore.Pet { + + enum Parameters { + + enum Id: PathParameter { + static let name = "petId" + static let description = "ID of pet to return" + static var required: Bool { true } + static var schema: Schema.Type { Schemas.Pet.Id.self } + } + } +} diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+PathItems.swift b/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+PathItems.swift new file mode 100644 index 0000000..e2cf13d --- /dev/null +++ b/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+PathItems.swift @@ -0,0 +1,32 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore.Pet { + + enum PathItems { + + enum Main: PathItem { + static var path: Path { "/pet" } + // + // static var post: Operation.Type? { Operations.Create.self } + } + + enum Identified: PathItem { + static var path: Path { Main.path / Parameters.Id.path } + static var parameters: [Parameter.Type] { + [ + Parameters.Id.self + ] + } + + static var get: Operation.Type? { Operations.Get.self } + } + + } +} diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Responses.swift b/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Responses.swift new file mode 100644 index 0000000..00f460b --- /dev/null +++ b/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Responses.swift @@ -0,0 +1,32 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI +import OpenAPIKit30 + +extension Petstore.Pet { + + enum Responses { + + enum Detail: Response { + static let description = "Successful operation" + // static var headers: [Header.Type] { + // [ + // Headers.CustomResponseHeader.self + // ] + // } + // static var schema: Schema.Type { Schemas.Pet.self } + + static var contents: [OpenAPI.ContentType: any Schema.Type] { + [ + .json: Schemas.Pet.self, + .xml: Schemas.Pet.self, + ] + } + } + } +} diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Schemas.swift b/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Schemas.swift new file mode 100644 index 0000000..bfa43ce --- /dev/null +++ b/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Schemas.swift @@ -0,0 +1,67 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore.Pet { + + enum Schemas { + + enum Pet: ObjectSchema { + + enum Id: Int64Schema { + + static var example: Int? { 10 } + } + + enum Name: TextSchema { + static var example: String? { "doggie" } + } + + enum PhotoUrls: ArraySchema { + + enum Item: TextSchema { + + } + + static var items: any Schema.Type { Item.self } + } + + enum Tags: ArraySchema { + static var items: any Schema.Type { + Petstore.Tag.Schemas.Tag.self + } + } + + enum Status: EnumSchema { + static var description: String { "pet status in the store" } + static var allowedValues: [String] { + [ + "available", + "pending", + "sold", + ] + } + } + + static var properties: [ObjectSchemaProperty] { + [ + .init("id", Id.self, required: false), + .init("name", Name.self), + .init( + "category", + Petstore.Category.Schemas.Category.self, + required: false + ), + .init("photoUrls", PhotoUrls.self), + .init("tags", Tags.self, required: false), + .init("status", Status.self, required: false), + ] + } + } + } +} diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Tags.swift b/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Tags.swift new file mode 100644 index 0000000..1c354e5 --- /dev/null +++ b/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Tags.swift @@ -0,0 +1,18 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore.Pet { + + enum Tags { + + enum Pet: Tag { + static let name = "Pet" + } + } +} diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet.swift b/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet.swift new file mode 100644 index 0000000..034a2f5 --- /dev/null +++ b/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet.swift @@ -0,0 +1,13 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore { + + enum Pet: Component {} +} diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Petstore.swift b/Tests/FeatherOpenAPIPluginTests/Petstore/Petstore.swift new file mode 100644 index 0000000..0af4787 --- /dev/null +++ b/Tests/FeatherOpenAPIPluginTests/Petstore/Petstore.swift @@ -0,0 +1,61 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI +import OpenAPIKit30 +import Foundation + +enum Petstore {} + +// https://petstore3.swagger.io/ +// https://petstore3.swagger.io/api/v3/openapi.json +// https://petstore3.swagger.io/api/v3/openapi.yaml + +struct PetstoreDocument: Document { + + let components: [Component.Type] + + init() { + self.components = [ + Petstore.User.self, + Petstore.Store.self, + Petstore.Pet.self, + Petstore.ApiResponse.self, + Petstore.Category.self, + Petstore.Tag.self, + ] + } + + func openAPIDocument() throws -> OpenAPI.Document { + try composedDocument( + info: .init( + title: "Swagger Petstore - OpenAPI 3.0", + description: """ + This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about\nSwagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.\n\nSome useful links:\n- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)\n- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml) + """, + termsOfService: .init(string: "https://swagger.io/terms/")!, + contact: .init( + email: "apiteam@swagger.io" + ), + license: .init( + name: "Apache 2.0", + url: .init( + string: + "https://www.apache.org/licenses/LICENSE-2.0.html" + )! + ), + version: "1.0.27", + ), + // TODO: external docs support. + servers: [ + .init( + url: .init(string: "/api/v3")! + ) + ], + ) + } +} diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Store/Store+Schemas.swift b/Tests/FeatherOpenAPIPluginTests/Petstore/Store/Store+Schemas.swift new file mode 100644 index 0000000..81146bf --- /dev/null +++ b/Tests/FeatherOpenAPIPluginTests/Petstore/Store/Store+Schemas.swift @@ -0,0 +1,61 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore.Store { + + enum Schemas { + + enum Order: ObjectSchema { + + enum Id: Int64Schema { + static var example: Int? { 10 } + } + + enum PetId: Int64Schema { + static var example: Int? { 198772 } + } + + enum Quantity: Int32Schema { + static var example: Int? { 7 } + } + + enum ShipDate: DateTimeSchema { + static var example: String? { nil } + } + + enum Status: EnumSchema { + static var description: String { "Order Status" } + static var allowedValues: [String] { + [ + "placed", + "approved", + "delivered", + ] + } + static var defaultValue: String? { nil } + static var example: String? { "approved" } + } + + enum Complete: BooleanSchema { + } + + static var properties: [ObjectSchemaProperty] { + [ + .init("id", Id.self), + .init("petId", PetId.self), + .init("quantity", Quantity.self), + .init("shipDate", ShipDate.self), + .init("status", Status.self), + .init("complete", Complete.self), + ] + } + } + + } +} diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Store/Store.swift b/Tests/FeatherOpenAPIPluginTests/Petstore/Store/Store.swift new file mode 100644 index 0000000..dfe2711 --- /dev/null +++ b/Tests/FeatherOpenAPIPluginTests/Petstore/Store/Store.swift @@ -0,0 +1,13 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore { + + enum Store: Component {} +} diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Tag/Tag+Schemas.swift b/Tests/FeatherOpenAPIPluginTests/Petstore/Tag/Tag+Schemas.swift new file mode 100644 index 0000000..00acd6f --- /dev/null +++ b/Tests/FeatherOpenAPIPluginTests/Petstore/Tag/Tag+Schemas.swift @@ -0,0 +1,31 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore.Tag { + + enum Schemas { + + enum Tag: ObjectSchema { + + enum Id: Int64Schema { + } + + enum Name: TextSchema { + + } + + static var properties: [ObjectSchemaProperty] { + [ + .init("id", Id.self), + .init("name", Name.self), + ] + } + } + } +} diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Tag/Tag.swift b/Tests/FeatherOpenAPIPluginTests/Petstore/Tag/Tag.swift new file mode 100644 index 0000000..c1eb486 --- /dev/null +++ b/Tests/FeatherOpenAPIPluginTests/Petstore/Tag/Tag.swift @@ -0,0 +1,13 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore { + + enum Tag: Component {} +} diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/User/User+Schemas.swift b/Tests/FeatherOpenAPIPluginTests/Petstore/User/User+Schemas.swift new file mode 100644 index 0000000..3f39c26 --- /dev/null +++ b/Tests/FeatherOpenAPIPluginTests/Petstore/User/User+Schemas.swift @@ -0,0 +1,63 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore.User { + + enum Schemas { + + enum User: ObjectSchema { + + enum Id: Int64Schema { + static var example: Int? { 0 } + } + + enum UserName: TextSchema { + static var example: String? { "theUser" } + } + + enum FirstName: TextSchema { + static var example: String? { "John" } + } + + enum LastName: TextSchema { + static var example: String? { "James" } + } + + enum Email: EmailSchema { + static var example: String? { "john@email.com" } + } + + enum Password: PasswordSchema { + static var example: String? { "12345" } + } + + enum Phone: TextSchema { + static var example: String? { "12345" } + } + + enum UserStatus: Int32Schema { + static var description: String? { "User Status" } + static var example: Int? { 1 } + } + + static var properties: [ObjectSchemaProperty] { + [ + .init("id", Id.self), + .init("username", UserName.self), + .init("firstName", FirstName.self), + .init("lastName", LastName.self), + .init("email", Email.self), + .init("password", Password.self), + .init("phone", Phone.self), + .init("userStatus", UserStatus.self), + ] + } + } + } +} diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/User/User.swift b/Tests/FeatherOpenAPIPluginTests/Petstore/User/User.swift new file mode 100644 index 0000000..7031b64 --- /dev/null +++ b/Tests/FeatherOpenAPIPluginTests/Petstore/User/User.swift @@ -0,0 +1,13 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore { + + enum User: Component {} +} diff --git a/Tests/FeatherOpenAPIPluginTests/PetstoreOpenAPIKitTests.swift b/Tests/FeatherOpenAPIPluginTests/PetstoreOpenAPIKitTests.swift new file mode 100644 index 0000000..f6e2035 --- /dev/null +++ b/Tests/FeatherOpenAPIPluginTests/PetstoreOpenAPIKitTests.swift @@ -0,0 +1,42 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import Foundation +import OpenAPIKit30 +import OpenAPIKitCore +import Yams +import Testing + +@testable import FeatherOpenAPI + +@Suite +struct PetstoreOpenAPIKitTests { + + @Test + func render() throws { + + let document = PetstoreDocument() + + let encoder = YAMLEncoder() + let openAPIDocument = try document.openAPIDocument() + + do { + _ = + try openAPIDocument + .locallyDereferenced() + .resolved() + } + catch { + Issue.record("\(error)") + return + } + + let output = try encoder.encode(openAPIDocument) + + print(output) + } +} diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift index 1c478d8..3f39f54 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift @@ -40,9 +40,9 @@ extension Example.Model { static var tag: Tag.Type { Tags.Main.self } static var summary: String { "Create example" } static var description: String { "Create example detail" } - static var requestBody: RequestBody.Type? { - RequestBodies.Create.self - } + static let requestBody: RequestBody.Type? = RequestBodies.Create + .self + static var responses: [OperationResponse] { [ .init(200, Responses.Detail.self) diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel.swift b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel.swift index 0c06cf1..b3a2797 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel.swift @@ -9,5 +9,24 @@ import FeatherOpenAPI extension Example { - enum Model: Component {} + enum Model: Component { + static func getComponentsOfType() -> [T] { + let prefixName = String(reflecting: self) + "." + return [ + Headers.self, + Operations.self, + Parameters.self, + PathItems.self, + RequestBodies.self, + Responses.self, + Schemas.self, + SecuritySchemes.self, + Tags.self, + ] + .compactMap { $0 as? T } + .filter { + String(reflecting: $0).hasPrefix(prefixName) + } + } + } } diff --git a/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift b/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift index 1bd9b57..986ec1b 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift @@ -9,5 +9,17 @@ import FeatherOpenAPI extension ExampleDuplicatedItem { - enum Model: Component {} + enum Model: Component { + + static func getComponentsOfType() -> [T] { + let prefixName = String(reflecting: self) + "." + return [ + Schemas.self + ] + .compactMap { $0 as? T } + .filter { + String(reflecting: $0).hasPrefix(prefixName) + } + } + } } diff --git a/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift b/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift index 0628156..888ff19 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift +++ b/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift @@ -9,5 +9,16 @@ import FeatherOpenAPI extension ExampleMissingParentItem { - enum Model: Component {} + enum Model: Component { + static func getComponentsOfType() -> [T] { + let prefixName = String(reflecting: self) + "." + return [ + Schemas.self + ] + .compactMap { $0 as? T } + .filter { + String(reflecting: $0).hasPrefix(prefixName) + } + } + } } diff --git a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift index bdf7602..0e30748 100644 --- a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift +++ b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift @@ -21,13 +21,13 @@ struct FeatherOpenAPIKitTests { let document = ExampleDocument() - #expect( - try document.schemas() - .contains { - $0.key.rawValue == "ExampleModelPatch" - && $0.value.description == "overridden" - } - ) + // #expect( + // try document.schemas() + // .contains { + // $0.key.rawValue == "ExampleModelPatch" + // && $0.value.description == "overridden" + // } + // ) let encoder = YAMLEncoder() let openAPIDocument = try document.openAPIDocument() @@ -44,16 +44,6 @@ struct FeatherOpenAPIKitTests { print(output) } - @Test - func schemaDescription() throws { - - struct IDSchema: NanoIDSchema {} - struct Foo: NanoIDSchema {} - - #expect(IDSchema.description == "ID description") - #expect(Foo.description == "Foo description") - } - @Test func duplicatedItem() throws { From c2120ff965a1cd4d1d7ac32291a0c240c0ea4ffe Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Thu, 22 Jan 2026 14:55:56 +0100 Subject: [PATCH 12/34] naming changes --- Package.swift | 56 ++++--- .../CommandBuilder.swift | 2 +- .../Callback.swift | 0 .../ComponentBuilder.swift | 0 .../Components.swift | 0 .../Contact.swift | 0 .../Content.swift | 0 .../Document.swift | 0 .../Example.swift | 0 .../ExternalDocumentation.swift | 0 .../Header.swift | 0 .../Info.swift | 0 .../License.swift | 0 .../Links.swift | 0 .../Location.swift | 0 .../Operation.swift | 0 .../Parameter.swift | 0 .../PathItem.swift | 0 .../RequestBody.swift | 0 .../Response.swift | 0 .../Schema.swift | 0 .../SecurityRequirement.swift | 0 .../SecurityScheme.swift | 0 .../Server.swift | 0 .../Tag.swift | 0 .../Variable.swift | 0 .../Extensions/JSONSchema+Enumeration.swift | 0 .../Extensions/JSONSchema+Text.swift | 0 .../OrderedDictionary+Composition.swift | 0 .../String+LowercasedFirstLetter.swift | 0 .../Interfaces/Component.swift | 0 .../Interfaces/Document.swift | 0 .../Interfaces/Header/Header.swift | 0 .../Interfaces/Identifiable.swift | 0 .../Interfaces/Operation/Operation.swift | 0 .../Operation/OperationResponse.swift | 0 .../Parameter/HeaderParameter.swift | 0 .../Interfaces/Parameter/Parameter.swift | 0 .../Interfaces/Parameter/PathParameter.swift | 0 .../Interfaces/Parameter/QueryParameter.swift | 0 .../Interfaces/PathItem/Path.swift | 0 .../Interfaces/PathItem/PathItem.swift | 0 .../Interfaces/RequestBody/BinaryBody.swift | 1 + .../Interfaces/RequestBody/FormBody.swift | 0 .../Interfaces/RequestBody/JSONBody.swift | 0 .../Interfaces/RequestBody/RequestBody.swift | 0 .../Interfaces/Response/BinaryResponse.swift | 0 .../Interfaces/Response/JSONResponse.swift | 0 .../Interfaces/Response/Response.swift | 2 +- .../Interfaces/Schema/ArraySchema.swift | 0 .../Interfaces/Schema/BooleanSchema.swift | 0 .../Interfaces/Schema/DateTimeSchema.swift | 0 .../Interfaces/Schema/DoubleSchema.swift | 0 .../Interfaces/Schema/EmailSchema.swift | 0 .../Interfaces/Schema/EnumSchema.swift | 0 .../Interfaces/Schema/FloatSchema.swift | 0 .../Interfaces/Schema/IDSchema.swift | 0 .../Interfaces/Schema/Int32Schema.swift | 0 .../Interfaces/Schema/Int64Schema.swift | 0 .../Interfaces/Schema/IntSchema.swift | 0 .../Interfaces/Schema/NanoIDSchema.swift | 0 .../Interfaces/Schema/NumberSchema.swift | 0 .../Interfaces/Schema/ObjectSchema.swift | 0 .../Schema/ObjectSchemaProperty.swift | 0 .../Interfaces/Schema/PasswordSchema.swift | 0 .../Interfaces/Schema/Schema.swift | 0 .../Interfaces/Schema/TextSchema.swift | 0 .../Interfaces/Schema/UUIDSchema.swift | 0 .../SecurityScheme/SecurityScheme.swift | 0 .../Interfaces/Tag/Tag.swift | 0 .../Entrypoint.swift | 6 +- .../TypeCollector.swift | 0 .../FeatherOpenAPIKitTests.swift | 155 ----------------- .../ApiResponse/ApiResponse+Schemas.swift | 2 +- .../Petstore/ApiResponse/ApiResponse.swift | 2 +- .../Petstore/Category/Category+Schemas.swift | 2 +- .../Petstore/Category/Category.swift | 2 +- .../Petstore/Pet/Pet+Operations.swift | 2 +- .../Petstore/Pet/Pet+Parameters.swift | 2 +- .../Petstore/Pet/Pet+PathItems.swift | 2 +- .../Petstore/Pet/Pet+Responses.swift | 2 +- .../Petstore/Pet/Pet+Schemas.swift | 2 +- .../Petstore/Pet/Pet+Tags.swift | 2 +- .../Petstore/Pet/Pet.swift | 2 +- .../Petstore/Petstore.swift | 2 +- .../Petstore/Store/Store+Schemas.swift | 2 +- .../Petstore/Store/Store.swift | 2 +- .../Petstore/Tag/Tag+Schemas.swift | 2 +- .../Petstore/Tag/Tag.swift | 2 +- .../Petstore/User/User+Schemas.swift | 2 +- .../Petstore/User/User.swift | 2 +- .../PetstoreOpenAPIKitTests.swift | 2 +- .../Example/Components/Example/Example.swift | 0 .../Example/Model/ExampleModel+Headers.swift | 2 +- .../Model/ExampleModel+Operations.swift | 2 +- .../Model/ExampleModel+Parameters.swift | 2 +- .../Model/ExampleModel+PathItems.swift | 2 +- .../Model/ExampleModel+RequestBodies.swift | 2 +- .../Model/ExampleModel+Responses.swift | 2 +- .../Example/Model/ExampleModel+Schemas.swift | 2 +- .../Model/ExampleModel+SecuritySchemes.swift | 2 +- .../Example/Model/ExampleModel+Tags.swift | 2 +- .../Example/Model/ExampleModel.swift | 2 +- .../ExampleDuplicatedItem.swift | 0 .../ExampleDuplicatedItemModel+Schemas.swift | 2 +- .../Model/ExampleDuplicatedItemModel.swift | 2 +- .../ExampleMissingParentItem.swift | 0 ...xampleMissingParentItemModel+Schemas.swift | 2 +- .../Model/ExampleMissingParentItemModel.swift | 2 +- .../Example/ExampleDocument.swift | 2 +- .../ExampleDuplicatedItemDocument.swift | 2 +- .../ExampleMissingParentItemDocument.swift | 2 +- .../FeatherOpenAPIKitTests.swift | 85 ++++++++++ .../PathComponentTests.swift | 2 +- .../Example/CreateExampleOperation.swift | 2 +- .../Example/GetExampleOperation.swift | 2 +- .../Example/TestOperation.swift | 2 +- .../FeatherOpenAPIKitTests.swift | 156 +++++++++++++----- .../Petstore/Petstore.swift | 2 +- 119 files changed, 275 insertions(+), 270 deletions(-) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/Callback.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/ComponentBuilder.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/Components.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/Contact.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/Content.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/Document.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/Example.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/ExternalDocumentation.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/Header.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/Info.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/License.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/Links.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/Location.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/Operation.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/Parameter.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/PathItem.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/RequestBody.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/Response.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/Schema.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/SecurityRequirement.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/SecurityScheme.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/Server.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/Tag.swift (100%) rename Sources/{FeatherOpenAPI30 => FeatherOpenAPI}/Variable.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Extensions/JSONSchema+Enumeration.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Extensions/JSONSchema+Text.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Extensions/OrderedDictionary+Composition.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Extensions/String+LowercasedFirstLetter.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Component.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Document.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Header/Header.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Identifiable.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Operation/Operation.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Operation/OperationResponse.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Parameter/HeaderParameter.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Parameter/Parameter.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Parameter/PathParameter.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Parameter/QueryParameter.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/PathItem/Path.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/PathItem/PathItem.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/RequestBody/BinaryBody.swift (99%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/RequestBody/FormBody.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/RequestBody/JSONBody.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/RequestBody/RequestBody.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Response/BinaryResponse.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Response/JSONResponse.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Response/Response.swift (94%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Schema/ArraySchema.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Schema/BooleanSchema.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Schema/DateTimeSchema.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Schema/DoubleSchema.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Schema/EmailSchema.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Schema/EnumSchema.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Schema/FloatSchema.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Schema/IDSchema.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Schema/Int32Schema.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Schema/Int64Schema.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Schema/IntSchema.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Schema/NanoIDSchema.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Schema/NumberSchema.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Schema/ObjectSchema.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Schema/ObjectSchemaProperty.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Schema/PasswordSchema.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Schema/Schema.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Schema/TextSchema.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Schema/UUIDSchema.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/SecurityScheme/SecurityScheme.swift (100%) rename Sources/{FeatherOpenAPI => FeatherOpenAPIKit}/Interfaces/Tag/Tag.swift (100%) rename Sources/{feather-openapi-generator => feather-openapikit-generator}/Entrypoint.swift (90%) rename Sources/{feather-openapi-generator => feather-openapikit-generator}/TypeCollector.swift (100%) delete mode 100644 Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift rename Tests/{FeatherOpenAPIPluginTests => FeatherOpenAPIKitPluginTests}/Petstore/ApiResponse/ApiResponse+Schemas.swift (96%) rename Tests/{FeatherOpenAPIPluginTests => FeatherOpenAPIKitPluginTests}/Petstore/ApiResponse/ApiResponse.swift (86%) rename Tests/{FeatherOpenAPIPluginTests => FeatherOpenAPIKitPluginTests}/Petstore/Category/Category+Schemas.swift (96%) rename Tests/{FeatherOpenAPIPluginTests => FeatherOpenAPIKitPluginTests}/Petstore/Category/Category.swift (85%) rename Tests/{FeatherOpenAPIPluginTests => FeatherOpenAPIKitPluginTests}/Petstore/Pet/Pet+Operations.swift (96%) rename Tests/{FeatherOpenAPIPluginTests => FeatherOpenAPIKitPluginTests}/Petstore/Pet/Pet+Parameters.swift (94%) rename Tests/{FeatherOpenAPIPluginTests => FeatherOpenAPIKitPluginTests}/Petstore/Pet/Pet+PathItems.swift (96%) rename Tests/{FeatherOpenAPIPluginTests => FeatherOpenAPIKitPluginTests}/Petstore/Pet/Pet+Responses.swift (96%) rename Tests/{FeatherOpenAPIPluginTests => FeatherOpenAPIKitPluginTests}/Petstore/Pet/Pet+Schemas.swift (98%) rename Tests/{FeatherOpenAPIPluginTests => FeatherOpenAPIKitPluginTests}/Petstore/Pet/Pet+Tags.swift (89%) rename Tests/{FeatherOpenAPIPluginTests => FeatherOpenAPIKitPluginTests}/Petstore/Pet/Pet.swift (85%) rename Tests/{FeatherOpenAPIPluginTests => FeatherOpenAPIKitPluginTests}/Petstore/Petstore.swift (98%) rename Tests/{FeatherOpenAPIPluginTests => FeatherOpenAPIKitPluginTests}/Petstore/Store/Store+Schemas.swift (98%) rename Tests/{FeatherOpenAPIPluginTests => FeatherOpenAPIKitPluginTests}/Petstore/Store/Store.swift (85%) rename Tests/{FeatherOpenAPIPluginTests => FeatherOpenAPIKitPluginTests}/Petstore/Tag/Tag+Schemas.swift (95%) rename Tests/{FeatherOpenAPIPluginTests => FeatherOpenAPIKitPluginTests}/Petstore/Tag/Tag.swift (85%) rename Tests/{FeatherOpenAPIPluginTests => FeatherOpenAPIKitPluginTests}/Petstore/User/User+Schemas.swift (98%) rename Tests/{FeatherOpenAPIPluginTests => FeatherOpenAPIKitPluginTests}/Petstore/User/User.swift (85%) rename Tests/{FeatherOpenAPIPluginTests => FeatherOpenAPIKitPluginTests}/PetstoreOpenAPIKitTests.swift (95%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/Components/Example/Example.swift (100%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/Components/Example/Model/ExampleModel+Headers.swift (95%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/Components/Example/Model/ExampleModel+Operations.swift (98%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/Components/Example/Model/ExampleModel+Parameters.swift (96%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/Components/Example/Model/ExampleModel+PathItems.swift (96%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/Components/Example/Model/ExampleModel+RequestBodies.swift (94%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/Components/Example/Model/ExampleModel+Responses.swift (97%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/Components/Example/Model/ExampleModel+Schemas.swift (99%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift (97%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/Components/Example/Model/ExampleModel+Tags.swift (91%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/Components/Example/Model/ExampleModel.swift (96%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/Components/ExampleDuplicatedItem/ExampleDuplicatedItem.swift (100%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift (96%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift (94%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/Components/ExampleMissingParentItem/ExampleMissingParentItem.swift (100%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift (95%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift (94%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/ExampleDocument.swift (97%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/ExampleDuplicatedItemDocument.swift (97%) rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/Example/ExampleMissingParentItemDocument.swift (97%) create mode 100644 Tests/FeatherOpenAPIKitTests/FeatherOpenAPIKitTests.swift rename Tests/{FeatherOpenAPITests => FeatherOpenAPIKitTests}/PathComponentTests.swift (98%) rename Tests/{FeatherOpenAPI30Tests => FeatherOpenAPITests}/Example/CreateExampleOperation.swift (98%) rename Tests/{FeatherOpenAPI30Tests => FeatherOpenAPITests}/Example/GetExampleOperation.swift (98%) rename Tests/{FeatherOpenAPI30Tests => FeatherOpenAPITests}/Example/TestOperation.swift (98%) rename Tests/{FeatherOpenAPI30Tests => FeatherOpenAPITests}/Petstore/Petstore.swift (98%) diff --git a/Package.swift b/Package.swift index 0430163..041bf93 100644 --- a/Package.swift +++ b/Package.swift @@ -34,16 +34,16 @@ let package = Package( ], products: [ .executable( - name: "feather-openapi-generator", - targets: ["feather-openapi-generator"] + name: "feather-openapikit-generator", + targets: ["feather-openapikit-generator"] ), .library( name: "FeatherOpenAPI", targets: ["FeatherOpenAPI"] ), .plugin( - name: "FeatherOpenAPIGenerator", - targets: ["FeatherOpenAPIGenerator"] + name: "FeatherOpenAPIKitGenerator", + targets: ["FeatherOpenAPIKitGenerator"] ), ], dependencies: [ @@ -53,62 +53,66 @@ let package = Package( ], targets: [ .plugin( - name: "FeatherOpenAPIGenerator", + name: "FeatherOpenAPIKitGenerator", capability: .buildTool(), dependencies: [ - .target(name: "feather-openapi-generator") + .target(name: "feather-openapikit-generator") ], ), // MARK: - - .target( - name: "FeatherOpenAPI", + .executableTarget( + name: "feather-openapikit-generator", dependencies: [ - .product(name: "OpenAPIKit", package: "OpenAPIKit"), - .product(name: "OpenAPIKit30", package: "OpenAPIKit"), - .product(name: "OpenAPIKitCompat", package: "OpenAPIKit"), - .product(name: "Yams", package: "yams"), + .product(name: "SwiftParser", package: "swift-syntax") ], - swiftSettings: defaultSwiftSettings + swiftSettings: defaultSwiftSettings, ), .target( - name: "FeatherOpenAPI30", + name: "FeatherOpenAPIKit", dependencies: [ .product(name: "OpenAPIKit30", package: "OpenAPIKit"), - .product(name: "OpenAPIKitCompat", package: "OpenAPIKit"), + ], swiftSettings: defaultSwiftSettings ), - .executableTarget( - name: "feather-openapi-generator", + .target( + name: "FeatherOpenAPI", dependencies: [ - .product(name: "SwiftParser", package: "swift-syntax") + .product(name: "OpenAPIKit30", package: "OpenAPIKit"), ], - swiftSettings: defaultSwiftSettings, + swiftSettings: defaultSwiftSettings ), + // MARK: - .testTarget( - name: "FeatherOpenAPITests", + name: "FeatherOpenAPIKitTests", dependencies: [ .product(name: "Yams", package: "Yams"), - .target(name: "FeatherOpenAPI"), + .product(name: "OpenAPIKit", package: "OpenAPIKit"), + .product(name: "OpenAPIKitCompat", package: "OpenAPIKit"), + .target(name: "FeatherOpenAPIKit"), ], swiftSettings: defaultSwiftSettings, ), .testTarget( - name: "FeatherOpenAPIPluginTests", + name: "FeatherOpenAPIKitPluginTests", dependencies: [ .product(name: "Yams", package: "Yams"), - .target(name: "FeatherOpenAPI"), + .product(name: "OpenAPIKit", package: "OpenAPIKit"), + .product(name: "OpenAPIKitCompat", package: "OpenAPIKit"), + .target(name: "FeatherOpenAPIKit"), ], swiftSettings: defaultSwiftSettings, plugins: [ - .plugin(name: "FeatherOpenAPIGenerator"), + .plugin(name: "FeatherOpenAPIKitGenerator"), ] ), .testTarget( - name: "FeatherOpenAPI30Tests", + name: "FeatherOpenAPITests", dependencies: [ .product(name: "Yams", package: "Yams"), - .target(name: "FeatherOpenAPI30"), + .product(name: "OpenAPIKit", package: "OpenAPIKit"), + .product(name: "OpenAPIKitCompat", package: "OpenAPIKit"), + .target(name: "FeatherOpenAPI"), ], swiftSettings: defaultSwiftSettings, ), diff --git a/Plugins/FeatherOpenAPIGenerator/CommandBuilder.swift b/Plugins/FeatherOpenAPIGenerator/CommandBuilder.swift index 585b400..05a75fd 100644 --- a/Plugins/FeatherOpenAPIGenerator/CommandBuilder.swift +++ b/Plugins/FeatherOpenAPIGenerator/CommandBuilder.swift @@ -31,7 +31,7 @@ struct CommandBuilder { return [ .buildCommand( displayName: "Generate component extension code", - executable: try tool("feather-openapi-generator").url, + executable: try tool("feather-openapikit-generator").url, arguments: [sourceDir, output.path(), targetName], environment: [:], inputFiles: [], diff --git a/Sources/FeatherOpenAPI30/Callback.swift b/Sources/FeatherOpenAPI/Callback.swift similarity index 100% rename from Sources/FeatherOpenAPI30/Callback.swift rename to Sources/FeatherOpenAPI/Callback.swift diff --git a/Sources/FeatherOpenAPI30/ComponentBuilder.swift b/Sources/FeatherOpenAPI/ComponentBuilder.swift similarity index 100% rename from Sources/FeatherOpenAPI30/ComponentBuilder.swift rename to Sources/FeatherOpenAPI/ComponentBuilder.swift diff --git a/Sources/FeatherOpenAPI30/Components.swift b/Sources/FeatherOpenAPI/Components.swift similarity index 100% rename from Sources/FeatherOpenAPI30/Components.swift rename to Sources/FeatherOpenAPI/Components.swift diff --git a/Sources/FeatherOpenAPI30/Contact.swift b/Sources/FeatherOpenAPI/Contact.swift similarity index 100% rename from Sources/FeatherOpenAPI30/Contact.swift rename to Sources/FeatherOpenAPI/Contact.swift diff --git a/Sources/FeatherOpenAPI30/Content.swift b/Sources/FeatherOpenAPI/Content.swift similarity index 100% rename from Sources/FeatherOpenAPI30/Content.swift rename to Sources/FeatherOpenAPI/Content.swift diff --git a/Sources/FeatherOpenAPI30/Document.swift b/Sources/FeatherOpenAPI/Document.swift similarity index 100% rename from Sources/FeatherOpenAPI30/Document.swift rename to Sources/FeatherOpenAPI/Document.swift diff --git a/Sources/FeatherOpenAPI30/Example.swift b/Sources/FeatherOpenAPI/Example.swift similarity index 100% rename from Sources/FeatherOpenAPI30/Example.swift rename to Sources/FeatherOpenAPI/Example.swift diff --git a/Sources/FeatherOpenAPI30/ExternalDocumentation.swift b/Sources/FeatherOpenAPI/ExternalDocumentation.swift similarity index 100% rename from Sources/FeatherOpenAPI30/ExternalDocumentation.swift rename to Sources/FeatherOpenAPI/ExternalDocumentation.swift diff --git a/Sources/FeatherOpenAPI30/Header.swift b/Sources/FeatherOpenAPI/Header.swift similarity index 100% rename from Sources/FeatherOpenAPI30/Header.swift rename to Sources/FeatherOpenAPI/Header.swift diff --git a/Sources/FeatherOpenAPI30/Info.swift b/Sources/FeatherOpenAPI/Info.swift similarity index 100% rename from Sources/FeatherOpenAPI30/Info.swift rename to Sources/FeatherOpenAPI/Info.swift diff --git a/Sources/FeatherOpenAPI30/License.swift b/Sources/FeatherOpenAPI/License.swift similarity index 100% rename from Sources/FeatherOpenAPI30/License.swift rename to Sources/FeatherOpenAPI/License.swift diff --git a/Sources/FeatherOpenAPI30/Links.swift b/Sources/FeatherOpenAPI/Links.swift similarity index 100% rename from Sources/FeatherOpenAPI30/Links.swift rename to Sources/FeatherOpenAPI/Links.swift diff --git a/Sources/FeatherOpenAPI30/Location.swift b/Sources/FeatherOpenAPI/Location.swift similarity index 100% rename from Sources/FeatherOpenAPI30/Location.swift rename to Sources/FeatherOpenAPI/Location.swift diff --git a/Sources/FeatherOpenAPI30/Operation.swift b/Sources/FeatherOpenAPI/Operation.swift similarity index 100% rename from Sources/FeatherOpenAPI30/Operation.swift rename to Sources/FeatherOpenAPI/Operation.swift diff --git a/Sources/FeatherOpenAPI30/Parameter.swift b/Sources/FeatherOpenAPI/Parameter.swift similarity index 100% rename from Sources/FeatherOpenAPI30/Parameter.swift rename to Sources/FeatherOpenAPI/Parameter.swift diff --git a/Sources/FeatherOpenAPI30/PathItem.swift b/Sources/FeatherOpenAPI/PathItem.swift similarity index 100% rename from Sources/FeatherOpenAPI30/PathItem.swift rename to Sources/FeatherOpenAPI/PathItem.swift diff --git a/Sources/FeatherOpenAPI30/RequestBody.swift b/Sources/FeatherOpenAPI/RequestBody.swift similarity index 100% rename from Sources/FeatherOpenAPI30/RequestBody.swift rename to Sources/FeatherOpenAPI/RequestBody.swift diff --git a/Sources/FeatherOpenAPI30/Response.swift b/Sources/FeatherOpenAPI/Response.swift similarity index 100% rename from Sources/FeatherOpenAPI30/Response.swift rename to Sources/FeatherOpenAPI/Response.swift diff --git a/Sources/FeatherOpenAPI30/Schema.swift b/Sources/FeatherOpenAPI/Schema.swift similarity index 100% rename from Sources/FeatherOpenAPI30/Schema.swift rename to Sources/FeatherOpenAPI/Schema.swift diff --git a/Sources/FeatherOpenAPI30/SecurityRequirement.swift b/Sources/FeatherOpenAPI/SecurityRequirement.swift similarity index 100% rename from Sources/FeatherOpenAPI30/SecurityRequirement.swift rename to Sources/FeatherOpenAPI/SecurityRequirement.swift diff --git a/Sources/FeatherOpenAPI30/SecurityScheme.swift b/Sources/FeatherOpenAPI/SecurityScheme.swift similarity index 100% rename from Sources/FeatherOpenAPI30/SecurityScheme.swift rename to Sources/FeatherOpenAPI/SecurityScheme.swift diff --git a/Sources/FeatherOpenAPI30/Server.swift b/Sources/FeatherOpenAPI/Server.swift similarity index 100% rename from Sources/FeatherOpenAPI30/Server.swift rename to Sources/FeatherOpenAPI/Server.swift diff --git a/Sources/FeatherOpenAPI30/Tag.swift b/Sources/FeatherOpenAPI/Tag.swift similarity index 100% rename from Sources/FeatherOpenAPI30/Tag.swift rename to Sources/FeatherOpenAPI/Tag.swift diff --git a/Sources/FeatherOpenAPI30/Variable.swift b/Sources/FeatherOpenAPI/Variable.swift similarity index 100% rename from Sources/FeatherOpenAPI30/Variable.swift rename to Sources/FeatherOpenAPI/Variable.swift diff --git a/Sources/FeatherOpenAPI/Extensions/JSONSchema+Enumeration.swift b/Sources/FeatherOpenAPIKit/Extensions/JSONSchema+Enumeration.swift similarity index 100% rename from Sources/FeatherOpenAPI/Extensions/JSONSchema+Enumeration.swift rename to Sources/FeatherOpenAPIKit/Extensions/JSONSchema+Enumeration.swift diff --git a/Sources/FeatherOpenAPI/Extensions/JSONSchema+Text.swift b/Sources/FeatherOpenAPIKit/Extensions/JSONSchema+Text.swift similarity index 100% rename from Sources/FeatherOpenAPI/Extensions/JSONSchema+Text.swift rename to Sources/FeatherOpenAPIKit/Extensions/JSONSchema+Text.swift diff --git a/Sources/FeatherOpenAPI/Extensions/OrderedDictionary+Composition.swift b/Sources/FeatherOpenAPIKit/Extensions/OrderedDictionary+Composition.swift similarity index 100% rename from Sources/FeatherOpenAPI/Extensions/OrderedDictionary+Composition.swift rename to Sources/FeatherOpenAPIKit/Extensions/OrderedDictionary+Composition.swift diff --git a/Sources/FeatherOpenAPI/Extensions/String+LowercasedFirstLetter.swift b/Sources/FeatherOpenAPIKit/Extensions/String+LowercasedFirstLetter.swift similarity index 100% rename from Sources/FeatherOpenAPI/Extensions/String+LowercasedFirstLetter.swift rename to Sources/FeatherOpenAPIKit/Extensions/String+LowercasedFirstLetter.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Component.swift b/Sources/FeatherOpenAPIKit/Interfaces/Component.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Component.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Component.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Document.swift b/Sources/FeatherOpenAPIKit/Interfaces/Document.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Document.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Document.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Header/Header.swift b/Sources/FeatherOpenAPIKit/Interfaces/Header/Header.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Header/Header.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Header/Header.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Identifiable.swift b/Sources/FeatherOpenAPIKit/Interfaces/Identifiable.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Identifiable.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Identifiable.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Operation/Operation.swift b/Sources/FeatherOpenAPIKit/Interfaces/Operation/Operation.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Operation/Operation.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Operation/Operation.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Operation/OperationResponse.swift b/Sources/FeatherOpenAPIKit/Interfaces/Operation/OperationResponse.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Operation/OperationResponse.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Operation/OperationResponse.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Parameter/HeaderParameter.swift b/Sources/FeatherOpenAPIKit/Interfaces/Parameter/HeaderParameter.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Parameter/HeaderParameter.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Parameter/HeaderParameter.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Parameter/Parameter.swift b/Sources/FeatherOpenAPIKit/Interfaces/Parameter/Parameter.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Parameter/Parameter.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Parameter/Parameter.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Parameter/PathParameter.swift b/Sources/FeatherOpenAPIKit/Interfaces/Parameter/PathParameter.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Parameter/PathParameter.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Parameter/PathParameter.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Parameter/QueryParameter.swift b/Sources/FeatherOpenAPIKit/Interfaces/Parameter/QueryParameter.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Parameter/QueryParameter.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Parameter/QueryParameter.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/PathItem/Path.swift b/Sources/FeatherOpenAPIKit/Interfaces/PathItem/Path.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/PathItem/Path.swift rename to Sources/FeatherOpenAPIKit/Interfaces/PathItem/Path.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/PathItem/PathItem.swift b/Sources/FeatherOpenAPIKit/Interfaces/PathItem/PathItem.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/PathItem/PathItem.swift rename to Sources/FeatherOpenAPIKit/Interfaces/PathItem/PathItem.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/RequestBody/BinaryBody.swift b/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/BinaryBody.swift similarity index 99% rename from Sources/FeatherOpenAPI/Interfaces/RequestBody/BinaryBody.swift rename to Sources/FeatherOpenAPIKit/Interfaces/RequestBody/BinaryBody.swift index 635922d..61ccec8 100644 --- a/Sources/FeatherOpenAPI/Interfaces/RequestBody/BinaryBody.swift +++ b/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/BinaryBody.swift @@ -25,3 +25,4 @@ extension BinaryBody { ) } } + diff --git a/Sources/FeatherOpenAPI/Interfaces/RequestBody/FormBody.swift b/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/FormBody.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/RequestBody/FormBody.swift rename to Sources/FeatherOpenAPIKit/Interfaces/RequestBody/FormBody.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/RequestBody/JSONBody.swift b/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/JSONBody.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/RequestBody/JSONBody.swift rename to Sources/FeatherOpenAPIKit/Interfaces/RequestBody/JSONBody.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/RequestBody/RequestBody.swift b/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/RequestBody.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/RequestBody/RequestBody.swift rename to Sources/FeatherOpenAPIKit/Interfaces/RequestBody/RequestBody.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Response/BinaryResponse.swift b/Sources/FeatherOpenAPIKit/Interfaces/Response/BinaryResponse.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Response/BinaryResponse.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Response/BinaryResponse.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Response/JSONResponse.swift b/Sources/FeatherOpenAPIKit/Interfaces/Response/JSONResponse.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Response/JSONResponse.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Response/JSONResponse.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Response/Response.swift b/Sources/FeatherOpenAPIKit/Interfaces/Response/Response.swift similarity index 94% rename from Sources/FeatherOpenAPI/Interfaces/Response/Response.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Response/Response.swift index b9eb3d7..f74b944 100644 --- a/Sources/FeatherOpenAPI/Interfaces/Response/Response.swift +++ b/Sources/FeatherOpenAPIKit/Interfaces/Response/Response.swift @@ -21,7 +21,7 @@ public extension Response { static var headers: [Header.Type] { [] } - static var contents: [OpenAPI.ContentType: FeatherOpenAPI.Schema.Type] { + static var contents: [OpenAPI.ContentType: Schema.Type] { [:] } diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/ArraySchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/ArraySchema.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Schema/ArraySchema.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Schema/ArraySchema.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/BooleanSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/BooleanSchema.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Schema/BooleanSchema.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Schema/BooleanSchema.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/DateTimeSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/DateTimeSchema.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Schema/DateTimeSchema.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Schema/DateTimeSchema.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/DoubleSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/DoubleSchema.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Schema/DoubleSchema.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Schema/DoubleSchema.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/EmailSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/EmailSchema.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Schema/EmailSchema.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Schema/EmailSchema.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/EnumSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/EnumSchema.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Schema/EnumSchema.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Schema/EnumSchema.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/FloatSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/FloatSchema.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Schema/FloatSchema.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Schema/FloatSchema.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/IDSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/IDSchema.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Schema/IDSchema.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Schema/IDSchema.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/Int32Schema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/Int32Schema.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Schema/Int32Schema.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Schema/Int32Schema.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/Int64Schema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/Int64Schema.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Schema/Int64Schema.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Schema/Int64Schema.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/IntSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/IntSchema.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Schema/IntSchema.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Schema/IntSchema.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/NanoIDSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/NanoIDSchema.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Schema/NanoIDSchema.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Schema/NanoIDSchema.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/NumberSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/NumberSchema.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Schema/NumberSchema.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Schema/NumberSchema.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/ObjectSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/ObjectSchema.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Schema/ObjectSchema.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Schema/ObjectSchema.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/ObjectSchemaProperty.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/ObjectSchemaProperty.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Schema/ObjectSchemaProperty.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Schema/ObjectSchemaProperty.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/PasswordSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/PasswordSchema.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Schema/PasswordSchema.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Schema/PasswordSchema.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/Schema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/Schema.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Schema/Schema.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Schema/Schema.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/TextSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/TextSchema.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Schema/TextSchema.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Schema/TextSchema.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Schema/UUIDSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/UUIDSchema.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Schema/UUIDSchema.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Schema/UUIDSchema.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/SecurityScheme/SecurityScheme.swift b/Sources/FeatherOpenAPIKit/Interfaces/SecurityScheme/SecurityScheme.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/SecurityScheme/SecurityScheme.swift rename to Sources/FeatherOpenAPIKit/Interfaces/SecurityScheme/SecurityScheme.swift diff --git a/Sources/FeatherOpenAPI/Interfaces/Tag/Tag.swift b/Sources/FeatherOpenAPIKit/Interfaces/Tag/Tag.swift similarity index 100% rename from Sources/FeatherOpenAPI/Interfaces/Tag/Tag.swift rename to Sources/FeatherOpenAPIKit/Interfaces/Tag/Tag.swift diff --git a/Sources/feather-openapi-generator/Entrypoint.swift b/Sources/feather-openapikit-generator/Entrypoint.swift similarity index 90% rename from Sources/feather-openapi-generator/Entrypoint.swift rename to Sources/feather-openapikit-generator/Entrypoint.swift index 5c39319..b453be2 100644 --- a/Sources/feather-openapi-generator/Entrypoint.swift +++ b/Sources/feather-openapikit-generator/Entrypoint.swift @@ -18,7 +18,7 @@ enum BuilderError: Error { struct Entrypoint { static func main() async throws { - print("feather-openapi-generator is running...") + print("feather-openapikit-generator is running...") guard CommandLine.arguments.count >= 3 else { throw BuilderError.wrongArgumentsNumber @@ -42,7 +42,7 @@ struct Entrypoint { let code = """ // generated on: \(dateString) - \(target == "FeatherOpenAPI" ? "" : "import FeatherOpenAPI") + \(target == "FeatherOpenAPIKit" ? "" : "import FeatherOpenAPIKit") extension Component { @@ -66,6 +66,6 @@ struct Entrypoint { try data.write(to: output, options: .atomic) - print("feather-openapi-generator finished.") + print("feather-openapikit-generator finished.") } } diff --git a/Sources/feather-openapi-generator/TypeCollector.swift b/Sources/feather-openapikit-generator/TypeCollector.swift similarity index 100% rename from Sources/feather-openapi-generator/TypeCollector.swift rename to Sources/feather-openapikit-generator/TypeCollector.swift diff --git a/Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift deleted file mode 100644 index 265b47f..0000000 --- a/Tests/FeatherOpenAPI30Tests/FeatherOpenAPIKitTests.swift +++ /dev/null @@ -1,155 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - -import Foundation -import OpenAPIKit -import OpenAPIKit30 -import OpenAPIKitCompat -import Yams -import Testing - -@testable import FeatherOpenAPI30 - -@Suite -struct FeatherOpenAPIKitTests { - - @Test - func ref() throws { - - struct UserId: SchemaRepresentable { - - func openAPISchema() -> OpenAPIKit30.JSONSchema { - fatalError() - } - } - - struct Reference { - - static func toSchemaRef() { - print(T.self) - } - } - - Reference.self.toSchemaRef() - } - - @Test - func example() throws { - - var builder = ComponentBuilder() - - let getExampleOperation = getExample(using: &builder) - // let createExampleOperation = createExample(using: &builder) - - let doc = Document( - info: Info( - title: "foo", - version: "1.0.0" - ), - paths: [ - "examples": PathItem( - summary: "Example related operations", - get: getExampleOperation, - // post: createExampleOperation - ) - ], - components: builder.components - ) - - let openAPIdoc = doc.openAPIDocument() - - let encoder = YAMLEncoder() - - do { - _ = - try openAPIdoc - .locallyDereferenced() - .resolved() - } - catch { - print(error) - throw error - } - - let result = try encoder.encode(openAPIdoc) - print(result) - - } - - func renderTest() throws { - - let doc = OpenAPIKit30.OpenAPI.Document( - info: .init( - title: "foo", - version: "3.0.0" - ), - servers: [], - paths: [ - "foo": .init( - .init( - summary: "foo", - get: .init( - requestBody: .init( - .component( - named: "foo" - ) - ), - responses: [:] - ), - ) - ) - ], - components: .init( - schemas: [ - "schemaID": .string( - format: .dateTime, - example: "Foo" - ) - ], - requestBodies: [ - "foo": .init( - description: "foo", - content: [ - .json: .init( - schemaReference: .component(named: "schemaID") - ) - ] - ) - ], - ) - ) - - let encoder = YAMLEncoder() - - do { - _ = - try doc - .locallyDereferenced() - .resolved() - } - catch { - print(type(of: error)) - print(error) - throw error - } - - let result = try encoder.encode(doc) - print("---- 3.0 ----") - print(result) - - let doc31 = doc.convert(to: .v3_1_0) - let result31 = try encoder.encode(doc31) - print("---- 3.1 ----") - print(result31) - - let doc32 = doc.convert(to: .v3_2_0) - let result32 = try encoder.encode(doc32) - print("---- 3.2 ----") - print(result32) - - } -} diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/ApiResponse/ApiResponse+Schemas.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/ApiResponse/ApiResponse+Schemas.swift similarity index 96% rename from Tests/FeatherOpenAPIPluginTests/Petstore/ApiResponse/ApiResponse+Schemas.swift rename to Tests/FeatherOpenAPIKitPluginTests/Petstore/ApiResponse/ApiResponse+Schemas.swift index 1a00fcb..a352222 100644 --- a/Tests/FeatherOpenAPIPluginTests/Petstore/ApiResponse/ApiResponse+Schemas.swift +++ b/Tests/FeatherOpenAPIKitPluginTests/Petstore/ApiResponse/ApiResponse+Schemas.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Petstore.ApiResponse { diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/ApiResponse/ApiResponse.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/ApiResponse/ApiResponse.swift similarity index 86% rename from Tests/FeatherOpenAPIPluginTests/Petstore/ApiResponse/ApiResponse.swift rename to Tests/FeatherOpenAPIKitPluginTests/Petstore/ApiResponse/ApiResponse.swift index caaf61f..b8c8f7f 100644 --- a/Tests/FeatherOpenAPIPluginTests/Petstore/ApiResponse/ApiResponse.swift +++ b/Tests/FeatherOpenAPIKitPluginTests/Petstore/ApiResponse/ApiResponse.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Petstore { diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Category/Category+Schemas.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Category/Category+Schemas.swift similarity index 96% rename from Tests/FeatherOpenAPIPluginTests/Petstore/Category/Category+Schemas.swift rename to Tests/FeatherOpenAPIKitPluginTests/Petstore/Category/Category+Schemas.swift index 8ab2ffc..2dc86ea 100644 --- a/Tests/FeatherOpenAPIPluginTests/Petstore/Category/Category+Schemas.swift +++ b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Category/Category+Schemas.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Petstore.Category { diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Category/Category.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Category/Category.swift similarity index 85% rename from Tests/FeatherOpenAPIPluginTests/Petstore/Category/Category.swift rename to Tests/FeatherOpenAPIKitPluginTests/Petstore/Category/Category.swift index 5da49e5..7baebe9 100644 --- a/Tests/FeatherOpenAPIPluginTests/Petstore/Category/Category.swift +++ b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Category/Category.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Petstore { diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Operations.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Operations.swift similarity index 96% rename from Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Operations.swift rename to Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Operations.swift index 8f34d17..a13a2ab 100644 --- a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Operations.swift +++ b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Operations.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Petstore.Pet { diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Parameters.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Parameters.swift similarity index 94% rename from Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Parameters.swift rename to Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Parameters.swift index 4d9fe49..d44c33a 100644 --- a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Parameters.swift +++ b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Parameters.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Petstore.Pet { diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+PathItems.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+PathItems.swift similarity index 96% rename from Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+PathItems.swift rename to Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+PathItems.swift index e2cf13d..5d6446f 100644 --- a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+PathItems.swift +++ b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+PathItems.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Petstore.Pet { diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Responses.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Responses.swift similarity index 96% rename from Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Responses.swift rename to Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Responses.swift index 00f460b..44f7f99 100644 --- a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Responses.swift +++ b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Responses.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPI +import FeatherOpenAPIKit import OpenAPIKit30 extension Petstore.Pet { diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Schemas.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Schemas.swift similarity index 98% rename from Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Schemas.swift rename to Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Schemas.swift index bfa43ce..a24aeba 100644 --- a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Schemas.swift +++ b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Schemas.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Petstore.Pet { diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Tags.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Tags.swift similarity index 89% rename from Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Tags.swift rename to Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Tags.swift index 1c354e5..4b7af69 100644 --- a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet+Tags.swift +++ b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Tags.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Petstore.Pet { diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet.swift similarity index 85% rename from Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet.swift rename to Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet.swift index 034a2f5..16d3158 100644 --- a/Tests/FeatherOpenAPIPluginTests/Petstore/Pet/Pet.swift +++ b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Petstore { diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Petstore.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Petstore.swift similarity index 98% rename from Tests/FeatherOpenAPIPluginTests/Petstore/Petstore.swift rename to Tests/FeatherOpenAPIKitPluginTests/Petstore/Petstore.swift index 0af4787..c1d2a85 100644 --- a/Tests/FeatherOpenAPIPluginTests/Petstore/Petstore.swift +++ b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Petstore.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPI +import FeatherOpenAPIKit import OpenAPIKit30 import Foundation diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Store/Store+Schemas.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Store/Store+Schemas.swift similarity index 98% rename from Tests/FeatherOpenAPIPluginTests/Petstore/Store/Store+Schemas.swift rename to Tests/FeatherOpenAPIKitPluginTests/Petstore/Store/Store+Schemas.swift index 81146bf..33e1951 100644 --- a/Tests/FeatherOpenAPIPluginTests/Petstore/Store/Store+Schemas.swift +++ b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Store/Store+Schemas.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Petstore.Store { diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Store/Store.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Store/Store.swift similarity index 85% rename from Tests/FeatherOpenAPIPluginTests/Petstore/Store/Store.swift rename to Tests/FeatherOpenAPIKitPluginTests/Petstore/Store/Store.swift index dfe2711..f5dca68 100644 --- a/Tests/FeatherOpenAPIPluginTests/Petstore/Store/Store.swift +++ b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Store/Store.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Petstore { diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Tag/Tag+Schemas.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Tag/Tag+Schemas.swift similarity index 95% rename from Tests/FeatherOpenAPIPluginTests/Petstore/Tag/Tag+Schemas.swift rename to Tests/FeatherOpenAPIKitPluginTests/Petstore/Tag/Tag+Schemas.swift index 00acd6f..d946d7d 100644 --- a/Tests/FeatherOpenAPIPluginTests/Petstore/Tag/Tag+Schemas.swift +++ b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Tag/Tag+Schemas.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Petstore.Tag { diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/Tag/Tag.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Tag/Tag.swift similarity index 85% rename from Tests/FeatherOpenAPIPluginTests/Petstore/Tag/Tag.swift rename to Tests/FeatherOpenAPIKitPluginTests/Petstore/Tag/Tag.swift index c1eb486..ccfb01f 100644 --- a/Tests/FeatherOpenAPIPluginTests/Petstore/Tag/Tag.swift +++ b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Tag/Tag.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Petstore { diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/User/User+Schemas.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/User/User+Schemas.swift similarity index 98% rename from Tests/FeatherOpenAPIPluginTests/Petstore/User/User+Schemas.swift rename to Tests/FeatherOpenAPIKitPluginTests/Petstore/User/User+Schemas.swift index 3f39c26..fe6f17a 100644 --- a/Tests/FeatherOpenAPIPluginTests/Petstore/User/User+Schemas.swift +++ b/Tests/FeatherOpenAPIKitPluginTests/Petstore/User/User+Schemas.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Petstore.User { diff --git a/Tests/FeatherOpenAPIPluginTests/Petstore/User/User.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/User/User.swift similarity index 85% rename from Tests/FeatherOpenAPIPluginTests/Petstore/User/User.swift rename to Tests/FeatherOpenAPIKitPluginTests/Petstore/User/User.swift index 7031b64..e3c244d 100644 --- a/Tests/FeatherOpenAPIPluginTests/Petstore/User/User.swift +++ b/Tests/FeatherOpenAPIKitPluginTests/Petstore/User/User.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Petstore { diff --git a/Tests/FeatherOpenAPIPluginTests/PetstoreOpenAPIKitTests.swift b/Tests/FeatherOpenAPIKitPluginTests/PetstoreOpenAPIKitTests.swift similarity index 95% rename from Tests/FeatherOpenAPIPluginTests/PetstoreOpenAPIKitTests.swift rename to Tests/FeatherOpenAPIKitPluginTests/PetstoreOpenAPIKitTests.swift index f6e2035..1108e98 100644 --- a/Tests/FeatherOpenAPIPluginTests/PetstoreOpenAPIKitTests.swift +++ b/Tests/FeatherOpenAPIKitPluginTests/PetstoreOpenAPIKitTests.swift @@ -11,7 +11,7 @@ import OpenAPIKitCore import Yams import Testing -@testable import FeatherOpenAPI +@testable import FeatherOpenAPIKit @Suite struct PetstoreOpenAPIKitTests { diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Example.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Example.swift similarity index 100% rename from Tests/FeatherOpenAPITests/Example/Components/Example/Example.swift rename to Tests/FeatherOpenAPIKitTests/Example/Components/Example/Example.swift diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Headers.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Headers.swift similarity index 95% rename from Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Headers.swift rename to Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Headers.swift index 01388d5..8e4ea3a 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Headers.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Headers.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Example.Model { diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Operations.swift similarity index 98% rename from Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift rename to Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Operations.swift index 3f39f54..0197095 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Operations.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Operations.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Example.Model { diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Parameters.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Parameters.swift similarity index 96% rename from Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Parameters.swift rename to Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Parameters.swift index 0b9cda4..9d84953 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Parameters.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Parameters.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Example.Model { diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+PathItems.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+PathItems.swift similarity index 96% rename from Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+PathItems.swift rename to Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+PathItems.swift index 6c8364e..a4db94e 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+PathItems.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+PathItems.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Example.Model { diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift similarity index 94% rename from Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift rename to Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift index d48aaa0..52687e0 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Example.Model { diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Responses.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Responses.swift similarity index 97% rename from Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Responses.swift rename to Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Responses.swift index c6d4fc6..a65ecca 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Responses.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Responses.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPI +import FeatherOpenAPIKit import OpenAPIKit30 extension Example.Model { diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Schemas.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Schemas.swift similarity index 99% rename from Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Schemas.swift rename to Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Schemas.swift index 083f584..6c2e7e2 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Schemas.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Schemas.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Example.Model { diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift similarity index 97% rename from Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift rename to Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift index 7550d3e..8cec42a 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPI +import FeatherOpenAPIKit import OpenAPIKit30 // shared operation security diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Tags.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Tags.swift similarity index 91% rename from Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Tags.swift rename to Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Tags.swift index b5a3ef1..5c039e3 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel+Tags.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Tags.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Example.Model { diff --git a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel.swift similarity index 96% rename from Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel.swift rename to Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel.swift index b3a2797..1247143 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/Example/Model/ExampleModel.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 25/01/2024. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension Example { diff --git a/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/ExampleDuplicatedItem.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/ExampleDuplicatedItem.swift similarity index 100% rename from Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/ExampleDuplicatedItem.swift rename to Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/ExampleDuplicatedItem.swift diff --git a/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift similarity index 96% rename from Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift rename to Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift index 09db0ae..e2a744a 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension ExampleDuplicatedItem.Model { diff --git a/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift similarity index 94% rename from Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift rename to Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift index 986ec1b..0608ea4 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 25/01/2024. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension ExampleDuplicatedItem { diff --git a/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/ExampleMissingParentItem.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/ExampleMissingParentItem.swift similarity index 100% rename from Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/ExampleMissingParentItem.swift rename to Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/ExampleMissingParentItem.swift diff --git a/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift similarity index 95% rename from Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift rename to Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift index 041d904..fd38c15 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension ExampleMissingParentItem.Model { diff --git a/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift similarity index 94% rename from Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift rename to Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift index 888ff19..4a737a0 100644 --- a/Tests/FeatherOpenAPITests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 25/01/2024. // -import FeatherOpenAPI +import FeatherOpenAPIKit extension ExampleMissingParentItem { diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift b/Tests/FeatherOpenAPIKitTests/Example/ExampleDocument.swift similarity index 97% rename from Tests/FeatherOpenAPITests/Example/ExampleDocument.swift rename to Tests/FeatherOpenAPIKitTests/Example/ExampleDocument.swift index c27e306..3c6cb14 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/ExampleDocument.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPI +import FeatherOpenAPIKit import OpenAPIKit30 import Foundation diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift b/Tests/FeatherOpenAPIKitTests/Example/ExampleDuplicatedItemDocument.swift similarity index 97% rename from Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift rename to Tests/FeatherOpenAPIKitTests/Example/ExampleDuplicatedItemDocument.swift index 191039d..7e4cd0e 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/ExampleDuplicatedItemDocument.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPI +import FeatherOpenAPIKit import OpenAPIKit30 import Foundation diff --git a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift b/Tests/FeatherOpenAPIKitTests/Example/ExampleMissingParentItemDocument.swift similarity index 97% rename from Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift rename to Tests/FeatherOpenAPIKitTests/Example/ExampleMissingParentItemDocument.swift index c1ea820..58fb3f1 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/ExampleMissingParentItemDocument.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPI +import FeatherOpenAPIKit import OpenAPIKit30 import Foundation diff --git a/Tests/FeatherOpenAPIKitTests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPIKitTests/FeatherOpenAPIKitTests.swift new file mode 100644 index 0000000..047e70f --- /dev/null +++ b/Tests/FeatherOpenAPIKitTests/FeatherOpenAPIKitTests.swift @@ -0,0 +1,85 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 20/01/2024. +// + +import Foundation +import OpenAPIKit30 +import OpenAPIKitCore +import Yams +import Testing + +@testable import FeatherOpenAPIKit + +@Suite +struct FeatherOpenAPIKitTests { + + @Test + func render() throws { + + let document = ExampleDocument() + + // #expect( + // try document.schemas() + // .contains { + // $0.key.rawValue == "ExampleModelPatch" + // && $0.value.description == "overridden" + // } + // ) + + let encoder = YAMLEncoder() + let openAPIDocument = try document.openAPIDocument() + do { + _ = try openAPIDocument.locallyDereferenced() + } + catch { + Issue.record("\(error)") + return + } + + let output = try encoder.encode(openAPIDocument) + + print(output) + } + + @Test + func duplicatedItem() throws { + + let document = ExampleDuplicatedItemDocument() + var errorMessage: String = "none" + + do { + let _ = try document.openAPIDocument() + } + catch let error as ComposeDocumentError { + errorMessage = error.message + } + + #expect( + errorMessage + == "Feather OpenAPI item id is duplicated: 'ExampleDuplicatedItemModelKey' (Did you forget to include override=true?)" + ) + } + + @Test + func missingParentItem() throws { + + let document = ExampleMissingParentItemItemDocument() + var errorMessage: String = "none" + + do { + let _ = try document.openAPIDocument() + } + catch let error as ComposeDocumentError { + errorMessage = error.message + } + + #expect( + errorMessage + == "Feather OpenAPI item 'ExampleMissingParentItemModelKey' is set as override but has no parent. (Are the component orders correct? Or are the IDs the same?)" + ) + } + +} diff --git a/Tests/FeatherOpenAPITests/PathComponentTests.swift b/Tests/FeatherOpenAPIKitTests/PathComponentTests.swift similarity index 98% rename from Tests/FeatherOpenAPITests/PathComponentTests.swift rename to Tests/FeatherOpenAPIKitTests/PathComponentTests.swift index 5d0ea30..394e8f7 100644 --- a/Tests/FeatherOpenAPITests/PathComponentTests.swift +++ b/Tests/FeatherOpenAPIKitTests/PathComponentTests.swift @@ -8,7 +8,7 @@ import Foundation import Testing -@testable import FeatherOpenAPI +@testable import FeatherOpenAPIKit fileprivate extension Path { diff --git a/Tests/FeatherOpenAPI30Tests/Example/CreateExampleOperation.swift b/Tests/FeatherOpenAPITests/Example/CreateExampleOperation.swift similarity index 98% rename from Tests/FeatherOpenAPI30Tests/Example/CreateExampleOperation.swift rename to Tests/FeatherOpenAPITests/Example/CreateExampleOperation.swift index 699a064..f1c05e0 100644 --- a/Tests/FeatherOpenAPI30Tests/Example/CreateExampleOperation.swift +++ b/Tests/FeatherOpenAPITests/Example/CreateExampleOperation.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 21.. // -import FeatherOpenAPI30 +import FeatherOpenAPI import OpenAPIKit30 func createExample( diff --git a/Tests/FeatherOpenAPI30Tests/Example/GetExampleOperation.swift b/Tests/FeatherOpenAPITests/Example/GetExampleOperation.swift similarity index 98% rename from Tests/FeatherOpenAPI30Tests/Example/GetExampleOperation.swift rename to Tests/FeatherOpenAPITests/Example/GetExampleOperation.swift index a27c251..7ba01ed 100644 --- a/Tests/FeatherOpenAPI30Tests/Example/GetExampleOperation.swift +++ b/Tests/FeatherOpenAPITests/Example/GetExampleOperation.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 21.. // -import FeatherOpenAPI30 +import FeatherOpenAPI import OpenAPIKit30 func getExample( diff --git a/Tests/FeatherOpenAPI30Tests/Example/TestOperation.swift b/Tests/FeatherOpenAPITests/Example/TestOperation.swift similarity index 98% rename from Tests/FeatherOpenAPI30Tests/Example/TestOperation.swift rename to Tests/FeatherOpenAPITests/Example/TestOperation.swift index 6b83239..7af819e 100644 --- a/Tests/FeatherOpenAPI30Tests/Example/TestOperation.swift +++ b/Tests/FeatherOpenAPITests/Example/TestOperation.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 21.. // -import FeatherOpenAPI30 +import FeatherOpenAPI import OpenAPIKit30 func testOperation( diff --git a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift index 0e30748..74bc345 100644 --- a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift +++ b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift @@ -6,8 +6,9 @@ // import Foundation +import OpenAPIKit import OpenAPIKit30 -import OpenAPIKitCore +import OpenAPIKitCompat import Yams import Testing @@ -17,69 +18,138 @@ import Testing struct FeatherOpenAPIKitTests { @Test - func render() throws { + func ref() throws { - let document = ExampleDocument() + struct UserId: SchemaRepresentable { - // #expect( - // try document.schemas() - // .contains { - // $0.key.rawValue == "ExampleModelPatch" - // && $0.value.description == "overridden" - // } - // ) - - let encoder = YAMLEncoder() - let openAPIDocument = try document.openAPIDocument() - do { - _ = try openAPIDocument.locallyDereferenced() - } - catch { - Issue.record("\(error)") - return + func openAPISchema() -> OpenAPIKit30.JSONSchema { + fatalError() + } } - let output = try encoder.encode(openAPIDocument) + struct Reference { - print(output) + static func toSchemaRef() { + print(T.self) + } + } + + Reference.self.toSchemaRef() } @Test - func duplicatedItem() throws { + func example() throws { + + var builder = ComponentBuilder() + + let getExampleOperation = getExample(using: &builder) + // let createExampleOperation = createExample(using: &builder) + + let doc = Document( + info: Info( + title: "foo", + version: "1.0.0" + ), + paths: [ + "examples": PathItem( + summary: "Example related operations", + get: getExampleOperation, + // post: createExampleOperation + ) + ], + components: builder.components + ) - let document = ExampleDuplicatedItemDocument() - var errorMessage: String = "none" + let openAPIdoc = doc.openAPIDocument() + + let encoder = YAMLEncoder() do { - let _ = try document.openAPIDocument() + _ = + try openAPIdoc + .locallyDereferenced() + .resolved() } - catch let error as ComposeDocumentError { - errorMessage = error.message + catch { + print(error) + throw error } - #expect( - errorMessage - == "Feather OpenAPI item id is duplicated: 'ExampleDuplicatedItemModelKey' (Did you forget to include override=true?)" - ) + let result = try encoder.encode(openAPIdoc) + print(result) + } - @Test - func missingParentItem() throws { + func renderTest() throws { + + let doc = OpenAPIKit30.OpenAPI.Document( + info: .init( + title: "foo", + version: "3.0.0" + ), + servers: [], + paths: [ + "foo": .init( + .init( + summary: "foo", + get: .init( + requestBody: .init( + .component( + named: "foo" + ) + ), + responses: [:] + ), + ) + ) + ], + components: .init( + schemas: [ + "schemaID": .string( + format: .dateTime, + example: "Foo" + ) + ], + requestBodies: [ + "foo": .init( + description: "foo", + content: [ + .json: .init( + schemaReference: .component(named: "schemaID") + ) + ] + ) + ], + ) + ) - let document = ExampleMissingParentItemItemDocument() - var errorMessage: String = "none" + let encoder = YAMLEncoder() do { - let _ = try document.openAPIDocument() + _ = + try doc + .locallyDereferenced() + .resolved() } - catch let error as ComposeDocumentError { - errorMessage = error.message + catch { + print(type(of: error)) + print(error) + throw error } - #expect( - errorMessage - == "Feather OpenAPI item 'ExampleMissingParentItemModelKey' is set as override but has no parent. (Are the component orders correct? Or are the IDs the same?)" - ) - } + let result = try encoder.encode(doc) + print("---- 3.0 ----") + print(result) + + let doc31 = doc.convert(to: .v3_1_0) + let result31 = try encoder.encode(doc31) + print("---- 3.1 ----") + print(result31) + let doc32 = doc.convert(to: .v3_2_0) + let result32 = try encoder.encode(doc32) + print("---- 3.2 ----") + print(result32) + + } } diff --git a/Tests/FeatherOpenAPI30Tests/Petstore/Petstore.swift b/Tests/FeatherOpenAPITests/Petstore/Petstore.swift similarity index 98% rename from Tests/FeatherOpenAPI30Tests/Petstore/Petstore.swift rename to Tests/FeatherOpenAPITests/Petstore/Petstore.swift index af20fe2..9881d93 100644 --- a/Tests/FeatherOpenAPI30Tests/Petstore/Petstore.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Petstore.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPI30 +import FeatherOpenAPI import OpenAPIKit30 func petstore( From fab8d982cedaa9a7d471e698d9a1a6f795810a77 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Fri, 23 Jan 2026 00:06:15 +0100 Subject: [PATCH 13/34] new approach [wip] --- Sources/FeatherOpenAPI/Callback.swift | 9 + Sources/FeatherOpenAPI/ComponentBuilder.swift | 6 +- Sources/FeatherOpenAPI/Components.swift | 26 ++- Sources/FeatherOpenAPI/Contact.swift | 9 + Sources/FeatherOpenAPI/Content.swift | 39 ---- Sources/FeatherOpenAPI/Content/Content.swift | 21 +++ .../FeatherOpenAPI/Content/ContentMap.swift | 14 ++ .../Content/ContentRepresentable.swift | 26 +++ .../Content/OpenAPIContentRepresentable.swift | 25 +++ Sources/FeatherOpenAPI/Document.swift | 9 + Sources/FeatherOpenAPI/Example.swift | 9 + .../ExternalDocumentation.swift | 9 + Sources/FeatherOpenAPI/Header.swift | 9 + Sources/FeatherOpenAPI/Info.swift | 10 + Sources/FeatherOpenAPI/License.swift | 11 ++ Sources/FeatherOpenAPI/Links.swift | 9 + Sources/FeatherOpenAPI/Location.swift | 9 + Sources/FeatherOpenAPI/Operation.swift | 10 + Sources/FeatherOpenAPI/Parameter.swift | 12 +- Sources/FeatherOpenAPI/PathItem.swift | 10 + Sources/FeatherOpenAPI/RequestBody.swift | 53 ------ .../BinaryRequestBodyRepresentable.swift | 26 +++ .../FormRequestBodyRepresentable.swift | 24 +++ .../JSONRequestBodyRepresentable.swift | 24 +++ .../RequestBodyRepresentable.swift | 29 +++ .../_OpenAPIRequestBodyRepresentable.swift | 20 ++ .../RequestBody/_RequestBodyID.swift | 17 ++ .../RequestBody/_RequestBodyProperties.swift | 27 +++ .../RequestBody/_RequestBodyReference.swift | 26 +++ Sources/FeatherOpenAPI/Response.swift | 57 ------ .../FeatherOpenAPI/Response/Response.swift | 36 ++++ .../_OpenAPIResponseRepresentable.swift | 19 ++ .../FeatherOpenAPI/Response/_ResponseID.swift | 17 ++ Sources/FeatherOpenAPI/Schema.swift | 178 ------------------ .../Schema/ArraySchemaRepresentable.swift | 45 +++++ .../Schema/BoolSchemaRepresentable.swift | 46 +++++ .../Schema/DoubleSchemaRepresentable.swift | 50 +++++ .../Schema/FloatSchemaRepresentable.swift | 51 +++++ .../Schema/Int32SchemaRepresentable.swift | 50 +++++ .../Schema/Int64SchemaRepresentable.swift | 50 +++++ .../Schema/IntSchemaRepresentable.swift | 50 +++++ .../Schema/ObjectSchemaRepresentable.swift | 46 +++++ .../Schema/StringSchemaRepresentable.swift | 51 +++++ .../Schema/_OpenAPISchemaRepresentable.swift | 20 ++ Sources/FeatherOpenAPI/Schema/_SchemaID.swift | 17 ++ .../Schema/_SchemaProperties.swift | 96 ++++++++++ .../Schema/_SchemaReference.swift | 26 +++ .../FeatherOpenAPI/SecurityRequirement.swift | 9 + Sources/FeatherOpenAPI/SecurityScheme.swift | 9 + Sources/FeatherOpenAPI/Server.swift | 9 + Sources/FeatherOpenAPI/Variable.swift | 9 + Sources/FeatherOpenAPI/_Identifiable.swift | 17 ++ Sources/FeatherOpenAPI/_TestObjects.swift | 57 ++++++ .../Example/CreateExampleOperation.swift | 14 +- .../Example/GetExampleOperation.swift | 9 +- .../Example/TestOperation.swift | 14 +- .../FeatherOpenAPIKitTests.swift | 9 +- .../Petstore/Petstore.swift | 74 +++++++- 58 files changed, 1304 insertions(+), 359 deletions(-) delete mode 100644 Sources/FeatherOpenAPI/Content.swift create mode 100644 Sources/FeatherOpenAPI/Content/Content.swift create mode 100644 Sources/FeatherOpenAPI/Content/ContentMap.swift create mode 100644 Sources/FeatherOpenAPI/Content/ContentRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Content/OpenAPIContentRepresentable.swift delete mode 100644 Sources/FeatherOpenAPI/RequestBody.swift create mode 100644 Sources/FeatherOpenAPI/RequestBody/BinaryRequestBodyRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/RequestBody/FormRequestBodyRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/RequestBody/JSONRequestBodyRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/RequestBody/RequestBodyRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/RequestBody/_OpenAPIRequestBodyRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/RequestBody/_RequestBodyID.swift create mode 100644 Sources/FeatherOpenAPI/RequestBody/_RequestBodyProperties.swift create mode 100644 Sources/FeatherOpenAPI/RequestBody/_RequestBodyReference.swift delete mode 100644 Sources/FeatherOpenAPI/Response.swift create mode 100644 Sources/FeatherOpenAPI/Response/Response.swift create mode 100644 Sources/FeatherOpenAPI/Response/_OpenAPIResponseRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Response/_ResponseID.swift delete mode 100644 Sources/FeatherOpenAPI/Schema.swift create mode 100644 Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Schema/_OpenAPISchemaRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Schema/_SchemaID.swift create mode 100644 Sources/FeatherOpenAPI/Schema/_SchemaProperties.swift create mode 100644 Sources/FeatherOpenAPI/Schema/_SchemaReference.swift create mode 100644 Sources/FeatherOpenAPI/_Identifiable.swift create mode 100644 Sources/FeatherOpenAPI/_TestObjects.swift diff --git a/Sources/FeatherOpenAPI/Callback.swift b/Sources/FeatherOpenAPI/Callback.swift index 9b1b5cc..4ce5a8e 100644 --- a/Sources/FeatherOpenAPI/Callback.swift +++ b/Sources/FeatherOpenAPI/Callback.swift @@ -22,6 +22,15 @@ public protocol CallbackRepresentable { func openAPICallback() -> Callback } +//extension OpenAPI.Components: ComponentsRepresentable { +// +// public func openAPIComponents() -> OpenAPI.Components { +// self +// } +//} + +// MARK: - + public struct Callback: CallbackRepresentable { public func openAPICallback() -> Callback { diff --git a/Sources/FeatherOpenAPI/ComponentBuilder.swift b/Sources/FeatherOpenAPI/ComponentBuilder.swift index 431f0c1..49c4580 100644 --- a/Sources/FeatherOpenAPI/ComponentBuilder.swift +++ b/Sources/FeatherOpenAPI/ComponentBuilder.swift @@ -43,7 +43,7 @@ public struct ComponentBuilder { public mutating func schema( id: String, - builder: (() -> SchemaRepresentable) + builder: (() -> OpenAPISchemaRepresentable) ) -> SchemaID { let id = SchemaID(id) let schema = builder() @@ -55,7 +55,7 @@ public struct ComponentBuilder { public mutating func response( id: String, - builder: (() -> ResponseRepresentable) + builder: (() -> OpenAPIResponseRepresentable) ) -> ResponseID { let id = ResponseID(id) let response = builder() @@ -67,7 +67,7 @@ public struct ComponentBuilder { public mutating func requestBody( id: String, - builder: (() -> RequestBodyRepresentable) + builder: (() -> OpenAPIRequestBodyRepresentable) ) -> RequestBodyID { let id = RequestBodyID(id) let requestBody = builder() diff --git a/Sources/FeatherOpenAPI/Components.swift b/Sources/FeatherOpenAPI/Components.swift index e79ee5a..77572e4 100644 --- a/Sources/FeatherOpenAPI/Components.swift +++ b/Sources/FeatherOpenAPI/Components.swift @@ -7,23 +7,29 @@ import OpenAPIKit30 -extension JSONSchema: SchemaRepresentable { - - public func openAPISchema() -> JSONSchema { self } -} public protocol ComponentsRepresentable { func openAPIComponents() -> OpenAPI.Components } +extension OpenAPI.Components: ComponentsRepresentable { + + public func openAPIComponents() -> OpenAPI.Components { + self + } +} + +// MARK: - + + public struct Components: ComponentsRepresentable { - public var schemas: OrderedDictionary + public var schemas: OrderedDictionary public var parameters: OrderedDictionary public var examples: OrderedDictionary - public var responses: OrderedDictionary + public var responses: OrderedDictionary public var requestBodies: - OrderedDictionary + OrderedDictionary public var headers: OrderedDictionary public var securitySchemes: OrderedDictionary @@ -32,12 +38,12 @@ public struct Components: ComponentsRepresentable { public var vendorExtensions: [String: AnyCodable] public init( - schemas: OrderedDictionary = [:], + schemas: OrderedDictionary = [:], parameters: OrderedDictionary = [:], examples: OrderedDictionary = [:], - responses: OrderedDictionary = [:], + responses: OrderedDictionary = [:], requestBodies: OrderedDictionary< - RequestBodyID, RequestBodyRepresentable + RequestBodyID, OpenAPIRequestBodyRepresentable > = [:], headers: OrderedDictionary = [:], securitySchemes: OrderedDictionary< diff --git a/Sources/FeatherOpenAPI/Contact.swift b/Sources/FeatherOpenAPI/Contact.swift index 4dc904f..6f448d1 100644 --- a/Sources/FeatherOpenAPI/Contact.swift +++ b/Sources/FeatherOpenAPI/Contact.swift @@ -12,6 +12,15 @@ public protocol ContactRepresentable { func openAPIContact() -> OpenAPI.Document.Info.Contact } +extension OpenAPI.Document.Info.Contact: ContactRepresentable { + + public func openAPIContact() -> OpenAPI.Document.Info.Contact { + self + } +} + +// MARK: - + public struct Contact: ContactRepresentable { public let name: String? diff --git a/Sources/FeatherOpenAPI/Content.swift b/Sources/FeatherOpenAPI/Content.swift deleted file mode 100644 index 32252d5..0000000 --- a/Sources/FeatherOpenAPI/Content.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public protocol ContentRepresentable { - func openAPIContent() -> OpenAPI.Content -} - -public struct Content: ContentRepresentable { - - public var schema: SchemaID - public var example: AnyCodable? - - public init( - schema: SchemaID, - example: AnyCodable? = nil - ) { - self.schema = schema - self.example = example - } - - public func openAPIContent() -> OpenAPI.Content { - .init( - schemaReference: .component(named: schema.rawValue), - example: example, - encoding: [:], - vendorExtensions: [:] - ) - } -} - -// -// -//public typealias Map = OrderedDictionary diff --git a/Sources/FeatherOpenAPI/Content/Content.swift b/Sources/FeatherOpenAPI/Content/Content.swift new file mode 100644 index 0000000..b61fae0 --- /dev/null +++ b/Sources/FeatherOpenAPI/Content/Content.swift @@ -0,0 +1,21 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + + +public struct Content: ContentRepresentable { + + public var schema: any OpenAPISchemaRepresentable { + _schema + } + + var _schema: T + + public init(_ schema: T) { + self._schema = schema + } + +} diff --git a/Sources/FeatherOpenAPI/Content/ContentMap.swift b/Sources/FeatherOpenAPI/Content/ContentMap.swift new file mode 100644 index 0000000..711439b --- /dev/null +++ b/Sources/FeatherOpenAPI/Content/ContentMap.swift @@ -0,0 +1,14 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public typealias ContentMap = OrderedDictionary +< + OpenAPI.ContentType, + OpenAPIContentRepresentable +> diff --git a/Sources/FeatherOpenAPI/Content/ContentRepresentable.swift b/Sources/FeatherOpenAPI/Content/ContentRepresentable.swift new file mode 100644 index 0000000..dda981c --- /dev/null +++ b/Sources/FeatherOpenAPI/Content/ContentRepresentable.swift @@ -0,0 +1,26 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol ContentRepresentable: + OpenAPIContentRepresentable +{ + var schema: OpenAPISchemaRepresentable { get } +} + +public extension ContentRepresentable { + + func openAPIContent() -> OpenAPI.Content { + .init( + schema: schema.openAPISchema(), + examples: nil, + encoding: nil, + vendorExtensions: [:] + ) + } +} diff --git a/Sources/FeatherOpenAPI/Content/OpenAPIContentRepresentable.swift b/Sources/FeatherOpenAPI/Content/OpenAPIContentRepresentable.swift new file mode 100644 index 0000000..e2cd692 --- /dev/null +++ b/Sources/FeatherOpenAPI/Content/OpenAPIContentRepresentable.swift @@ -0,0 +1,25 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol OpenAPIContentRepresentable { + func openAPIContent() -> OpenAPI.Content +} + +extension OpenAPI.Content: OpenAPIContentRepresentable { + + public func openAPIContent() -> OpenAPI.Content { + self + } +} + +// MARK: - + + + + diff --git a/Sources/FeatherOpenAPI/Document.swift b/Sources/FeatherOpenAPI/Document.swift index d8754b0..9c7f2f2 100644 --- a/Sources/FeatherOpenAPI/Document.swift +++ b/Sources/FeatherOpenAPI/Document.swift @@ -11,6 +11,15 @@ public protocol DocumentRepresentable { func openAPIDocument() -> OpenAPI.Document } +extension OpenAPI.Document: DocumentRepresentable { + + public func openAPIDocument() -> OpenAPI.Document { + self + } +} + +// MARK: - + public struct Document: DocumentRepresentable { public var info: InfoRepresentable diff --git a/Sources/FeatherOpenAPI/Example.swift b/Sources/FeatherOpenAPI/Example.swift index 4fb78cb..758d5cf 100644 --- a/Sources/FeatherOpenAPI/Example.swift +++ b/Sources/FeatherOpenAPI/Example.swift @@ -22,6 +22,15 @@ public protocol ExampleRepresentable { func openAPIExample() -> OpenAPI.Example } +extension OpenAPI.Example: ExampleRepresentable { + + public func openAPIExample() -> OpenAPI.Example { + self + } +} + +// MARK: - + public struct Example: ExampleRepresentable { public var summary: String? diff --git a/Sources/FeatherOpenAPI/ExternalDocumentation.swift b/Sources/FeatherOpenAPI/ExternalDocumentation.swift index a7fa9f9..ebede2c 100644 --- a/Sources/FeatherOpenAPI/ExternalDocumentation.swift +++ b/Sources/FeatherOpenAPI/ExternalDocumentation.swift @@ -17,6 +17,15 @@ public protocol ExternalDocumentationRepresentable { func openAPIExternalDocumentation() -> OpenAPI.ExternalDocumentation } +extension OpenAPI.ExternalDocumentation: ExternalDocumentationRepresentable { + + public func openAPIExternalDocumentation() -> OpenAPI.ExternalDocumentation { + self + } +} + +// MARK: - + public struct ExternalDocumentation: ExternalDocumentationRepresentable { public var url: String public var description: String? diff --git a/Sources/FeatherOpenAPI/Header.swift b/Sources/FeatherOpenAPI/Header.swift index 5e22c83..ff03ac3 100644 --- a/Sources/FeatherOpenAPI/Header.swift +++ b/Sources/FeatherOpenAPI/Header.swift @@ -22,6 +22,15 @@ public protocol HeaderRepresentable { func openAPIHeader() -> OpenAPI.Header } +extension OpenAPI.Header: HeaderRepresentable { + + public func openAPIHeader() -> OpenAPI.Header { + self + } +} + +// MARK: - + public struct Header: HeaderRepresentable { public var schema: SchemaID diff --git a/Sources/FeatherOpenAPI/Info.swift b/Sources/FeatherOpenAPI/Info.swift index a532e5e..ca89c86 100644 --- a/Sources/FeatherOpenAPI/Info.swift +++ b/Sources/FeatherOpenAPI/Info.swift @@ -12,6 +12,16 @@ public protocol InfoRepresentable { func openAPIInfo() -> OpenAPI.Document.Info } +extension OpenAPI.Document.Info: InfoRepresentable { + + public func openAPIInfo() -> OpenAPI.Document.Info { + self + } +} + +// MARK: - + + public struct Info: InfoRepresentable { public var title: String public var description: String? diff --git a/Sources/FeatherOpenAPI/License.swift b/Sources/FeatherOpenAPI/License.swift index 728cffd..ad5acda 100644 --- a/Sources/FeatherOpenAPI/License.swift +++ b/Sources/FeatherOpenAPI/License.swift @@ -11,6 +11,17 @@ public protocol LicenseRepresentable { func openAPILicense() -> OpenAPI.Document.Info.License } +extension OpenAPI.Document.Info.License: LicenseRepresentable { + + public func openAPILicense() -> OpenAPI.Document.Info.License { + self + } +} + +// MARK: - + + + public struct License: LicenseRepresentable { public let name: String public let url: URLRepresentable? diff --git a/Sources/FeatherOpenAPI/Links.swift b/Sources/FeatherOpenAPI/Links.swift index 64a7500..76e2595 100644 --- a/Sources/FeatherOpenAPI/Links.swift +++ b/Sources/FeatherOpenAPI/Links.swift @@ -22,6 +22,15 @@ public protocol LinkRepresentable { func openAPILink() -> OpenAPI.Link } +extension OpenAPI.Link: LinkRepresentable { + + public func openAPILink() -> OpenAPI.Link { + self + } +} + +// MARK: - + public struct Link: LinkRepresentable { public func openAPILink() -> OpenAPI.Link { diff --git a/Sources/FeatherOpenAPI/Location.swift b/Sources/FeatherOpenAPI/Location.swift index 20d1e82..42865b7 100644 --- a/Sources/FeatherOpenAPI/Location.swift +++ b/Sources/FeatherOpenAPI/Location.swift @@ -15,6 +15,15 @@ public protocol URLRepresentable { func url() -> URL } +extension URL: URLRepresentable { + + public func url() -> URL { + self + } +} + +// MARK: - + public struct Location: URLRepresentable { public var string: String diff --git a/Sources/FeatherOpenAPI/Operation.swift b/Sources/FeatherOpenAPI/Operation.swift index a61e780..f53648e 100644 --- a/Sources/FeatherOpenAPI/Operation.swift +++ b/Sources/FeatherOpenAPI/Operation.swift @@ -11,6 +11,16 @@ public protocol OperationRepresentable { func openAPIOperation() -> OpenAPI.Operation } + +extension OpenAPI.Operation: OperationRepresentable { + + public func openAPIOperation() -> OpenAPI.Operation { + self + } +} + +// MARK: - + public struct Operation: OperationRepresentable { public var tags: [String]? diff --git a/Sources/FeatherOpenAPI/Parameter.swift b/Sources/FeatherOpenAPI/Parameter.swift index 270c35f..64f7f18 100644 --- a/Sources/FeatherOpenAPI/Parameter.swift +++ b/Sources/FeatherOpenAPI/Parameter.swift @@ -22,6 +22,16 @@ public protocol ParameterRepresentable { func openAPIParameter() -> OpenAPI.Parameter } +extension OpenAPI.Parameter: ParameterRepresentable { + + public func openAPIParameter() -> OpenAPI.Parameter { + self + } +} + +// MARK: - + + public struct Parameter: ParameterRepresentable { public var name: String @@ -51,7 +61,7 @@ public struct Parameter: ParameterRepresentable { .init( name: name, context: context, - schemaReference: .component(named: schema.rawValue), + schema: .reference(.component(named: schema.rawValue), required: true), description: description, deprecated: deprecated, vendorExtensions: vendorExtensions diff --git a/Sources/FeatherOpenAPI/PathItem.swift b/Sources/FeatherOpenAPI/PathItem.swift index 34d1783..94c37a2 100644 --- a/Sources/FeatherOpenAPI/PathItem.swift +++ b/Sources/FeatherOpenAPI/PathItem.swift @@ -11,6 +11,16 @@ public protocol PathItemRepresentable { func openAPIPathItem() -> OpenAPI.PathItem } +extension OpenAPI.PathItem: PathItemRepresentable { + + public func openAPIPathItem() -> OpenAPI.PathItem { + self + } +} + +// MARK: - + + public struct PathItem: PathItemRepresentable { public var summary: String? diff --git a/Sources/FeatherOpenAPI/RequestBody.swift b/Sources/FeatherOpenAPI/RequestBody.swift deleted file mode 100644 index e146519..0000000 --- a/Sources/FeatherOpenAPI/RequestBody.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public struct RequestBodyID: Hashable { - - public var rawValue: String - - public init( - _ rawValue: String - ) { - self.rawValue = rawValue - } -} - -public protocol RequestBodyRepresentable { - func openAPIRequestBody() -> OpenAPI.Request -} - -public struct RequestBody: RequestBodyRepresentable { - - public var description: String? - public var content: - OrderedDictionary - public var required: Bool - public var vendorExtensions: [String: AnyCodable] - - public init( - description: String? = nil, - content: OrderedDictionary, - required: Bool = false, - vendorExtensions: [String: AnyCodable] = [:] - ) { - self.description = description - self.content = content - self.required = required - self.vendorExtensions = vendorExtensions - } - - public func openAPIRequestBody() -> OpenAPI.Request { - .init( - description: description, - content: content.mapValues { $0.openAPIContent() }, - required: required, - vendorExtensions: vendorExtensions - ) - } -} diff --git a/Sources/FeatherOpenAPI/RequestBody/BinaryRequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/BinaryRequestBodyRepresentable.swift new file mode 100644 index 0000000..7c16e4a --- /dev/null +++ b/Sources/FeatherOpenAPI/RequestBody/BinaryRequestBodyRepresentable.swift @@ -0,0 +1,26 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +public protocol BinaryRequestBodyRepresentable: RequestBodyRepresentable { + +} + +public extension BinaryRequestBodyRepresentable { + + var contentMap: ContentMap { + [ + .other("application/octet-stream"): Content( + JSONSchema.string( + format: .binary + ) + ) + ] + } +} + diff --git a/Sources/FeatherOpenAPI/RequestBody/FormRequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/FormRequestBodyRepresentable.swift new file mode 100644 index 0000000..46e1332 --- /dev/null +++ b/Sources/FeatherOpenAPI/RequestBody/FormRequestBodyRepresentable.swift @@ -0,0 +1,24 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +public protocol FormRequestBodyRepresentable: RequestBodyRepresentable { + associatedtype SchemaType: OpenAPISchemaRepresentable + + var schema: SchemaType { get } +} + +public extension FormRequestBodyRepresentable { + + var contentMap: ContentMap { + [ + .form: Content(schema) + ] + } +} + diff --git a/Sources/FeatherOpenAPI/RequestBody/JSONRequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/JSONRequestBodyRepresentable.swift new file mode 100644 index 0000000..68de6c0 --- /dev/null +++ b/Sources/FeatherOpenAPI/RequestBody/JSONRequestBodyRepresentable.swift @@ -0,0 +1,24 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +public protocol JSONRequestBodyRepresentable: RequestBodyRepresentable { + associatedtype SchemaType: OpenAPISchemaRepresentable + + var schema: SchemaType { get } +} + +public extension JSONRequestBodyRepresentable { + + var contentMap: ContentMap { + [ + .json: Content(schema) + ] + } +} + diff --git a/Sources/FeatherOpenAPI/RequestBody/RequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/RequestBodyRepresentable.swift new file mode 100644 index 0000000..e7097ef --- /dev/null +++ b/Sources/FeatherOpenAPI/RequestBody/RequestBodyRepresentable.swift @@ -0,0 +1,29 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +public protocol RequestBodyRepresentable: + Identifiable, + OpenAPIRequestBodyRepresentable, + RequestBodyPropertyDescription, + RequestBodyPropertyRequired +{ + var contentMap: ContentMap { get } +} + +extension RequestBodyRepresentable { + + public func openAPIRequestBody() -> OpenAPI.Request { + .init( + description: description, + content: contentMap.mapValues { $0.openAPIContent() }, + required: required, + vendorExtensions: [:] + ) + } +} diff --git a/Sources/FeatherOpenAPI/RequestBody/_OpenAPIRequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/_OpenAPIRequestBodyRepresentable.swift new file mode 100644 index 0000000..7290514 --- /dev/null +++ b/Sources/FeatherOpenAPI/RequestBody/_OpenAPIRequestBodyRepresentable.swift @@ -0,0 +1,20 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol OpenAPIRequestBodyRepresentable { + func openAPIRequestBody() -> OpenAPI.Request +} + +extension OpenAPI.Request: OpenAPIRequestBodyRepresentable { + + public func openAPIRequestBody() -> OpenAPI.Request { + self + } +} + diff --git a/Sources/FeatherOpenAPI/RequestBody/_RequestBodyID.swift b/Sources/FeatherOpenAPI/RequestBody/_RequestBodyID.swift new file mode 100644 index 0000000..a486a96 --- /dev/null +++ b/Sources/FeatherOpenAPI/RequestBody/_RequestBodyID.swift @@ -0,0 +1,17 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +public struct RequestBodyID: Hashable { + + public var rawValue: String + + public init( + _ rawValue: String + ) { + self.rawValue = rawValue + } +} diff --git a/Sources/FeatherOpenAPI/RequestBody/_RequestBodyProperties.swift b/Sources/FeatherOpenAPI/RequestBody/_RequestBodyProperties.swift new file mode 100644 index 0000000..1c64c04 --- /dev/null +++ b/Sources/FeatherOpenAPI/RequestBody/_RequestBodyProperties.swift @@ -0,0 +1,27 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + + +// MARK: - description + +public protocol RequestBodyPropertyDescription { + var description: String? { get } +} + +public extension RequestBodyPropertyDescription { + var description: String? { nil } +} + +// MARK: - required + +public protocol RequestBodyPropertyRequired { + var required: Bool { get } +} + +public extension RequestBodyPropertyRequired { + var required: Bool { true } +} diff --git a/Sources/FeatherOpenAPI/RequestBody/_RequestBodyReference.swift b/Sources/FeatherOpenAPI/RequestBody/_RequestBodyReference.swift new file mode 100644 index 0000000..f0ebac3 --- /dev/null +++ b/Sources/FeatherOpenAPI/RequestBody/_RequestBodyReference.swift @@ -0,0 +1,26 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +//public struct RequestBodyReference: RequestBodyRepresentable { +// +// public var id: String +// public var required: Bool +// +// public init( +// _ type: T, +// required: Bool = true +// ) { +// self.id = type.openAPIIdentifier +// self.required = required +// } +// +// public func openAPIRequestBody() -> OpenAPI.Request { +// . +// } +//} diff --git a/Sources/FeatherOpenAPI/Response.swift b/Sources/FeatherOpenAPI/Response.swift deleted file mode 100644 index d59b0a4..0000000 --- a/Sources/FeatherOpenAPI/Response.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public struct ResponseID: Hashable { - - public var rawValue: String - - public init( - _ rawValue: String - ) { - self.rawValue = rawValue - } -} - -public protocol ResponseRepresentable { - func openAPIResponse() -> OpenAPI.Response -} - -public struct Response: ResponseRepresentable { - - public var description: String - public var headers: OrderedDictionary - public var content: - OrderedDictionary - public var vendorExtensions: [String: AnyCodable] - - public init( - description: String, - headers: OrderedDictionary = [:], - content: OrderedDictionary = - [:], - vendorExtensions: [String: AnyCodable] = [:] - ) { - self.description = description - self.headers = headers - self.content = content - self.vendorExtensions = vendorExtensions - } - - public func openAPIResponse() -> OpenAPI.Response { - .init( - description: description, - headers: headers.mapValues { - .init(.component(named: $0.rawValue)) - }, - content: content.mapValues { $0.openAPIContent() }, - links: [:], - vendorExtensions: vendorExtensions - ) - } -} diff --git a/Sources/FeatherOpenAPI/Response/Response.swift b/Sources/FeatherOpenAPI/Response/Response.swift new file mode 100644 index 0000000..3fc0266 --- /dev/null +++ b/Sources/FeatherOpenAPI/Response/Response.swift @@ -0,0 +1,36 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public typealias HeaderMap = OrderedDictionary< + String, + HeaderRepresentable +> + +public protocol ResponseRepresentable: + OpenAPIResponseRepresentable +{ + var description: String { get } + var headerMap: HeaderMap { get } + var contentMap: ContentMap { get } +} + +public extension ResponseRepresentable { + + var headerMap: HeaderMap { [:] } + + func openAPIResponse() -> OpenAPI.Response { + .init( + description: description, + headers: headerMap.mapValues { .init($0.openAPIHeader()) }, + content: contentMap.mapValues { $0.openAPIContent() }, + links: [:], + vendorExtensions: [:] + ) + } +} diff --git a/Sources/FeatherOpenAPI/Response/_OpenAPIResponseRepresentable.swift b/Sources/FeatherOpenAPI/Response/_OpenAPIResponseRepresentable.swift new file mode 100644 index 0000000..0c81a50 --- /dev/null +++ b/Sources/FeatherOpenAPI/Response/_OpenAPIResponseRepresentable.swift @@ -0,0 +1,19 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +public protocol OpenAPIResponseRepresentable { + func openAPIResponse() -> OpenAPI.Response +} + +extension OpenAPI.Response: OpenAPIResponseRepresentable { + + public func openAPIResponse() -> OpenAPI.Response { + self + } +} diff --git a/Sources/FeatherOpenAPI/Response/_ResponseID.swift b/Sources/FeatherOpenAPI/Response/_ResponseID.swift new file mode 100644 index 0000000..fef7c07 --- /dev/null +++ b/Sources/FeatherOpenAPI/Response/_ResponseID.swift @@ -0,0 +1,17 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +public struct ResponseID: Hashable { + + public var rawValue: String + + public init( + _ rawValue: String + ) { + self.rawValue = rawValue + } +} diff --git a/Sources/FeatherOpenAPI/Schema.swift b/Sources/FeatherOpenAPI/Schema.swift deleted file mode 100644 index e761316..0000000 --- a/Sources/FeatherOpenAPI/Schema.swift +++ /dev/null @@ -1,178 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public struct SchemaID: Hashable { - - public var rawValue: String - - public init( - _ rawValue: String - ) { - self.rawValue = rawValue - } -} - -public protocol SchemaRepresentable { - - func openAPISchema() -> JSONSchema -} - -public struct StringSchema: SchemaRepresentable { - - public var format: JSONTypeFormat.StringFormat - public var required: Bool - public var nullable: Bool? - // public var permissions: Permissions? - public var deprecated: Bool? - public var title: String? - public var description: String? - public var discriminator: OpenAPI.Discriminator? - public var externalDocs: OpenAPI.ExternalDocumentation? - public var minLength: Int? - public var maxLength: Int? - public var pattern: String? - public var allowedValues: [AnyCodable]? - public var defaultValue: AnyCodable? - public var example: AnyCodable? - - public init( - format: JSONTypeFormat.StringFormat = .generic, - required: Bool = false, - nullable: Bool? = nil, - // permissions: Permissions? = nil, - deprecated: Bool? = nil, - title: String? = nil, - description: String? = nil, - discriminator: OpenAPI.Discriminator? = nil, - externalDocs: OpenAPI.ExternalDocumentation? = nil, - minLength: Int? = nil, - maxLength: Int? = nil, - pattern: String? = nil, - allowedValues: [AnyCodable]? = nil, - defaultValue: AnyCodable? = nil, - example: AnyCodable? = nil - ) { - self.format = format - self.required = required - self.nullable = nullable - // self.permissions = permissions - self.deprecated = deprecated - self.title = title - self.description = description - self.discriminator = discriminator - self.externalDocs = externalDocs - self.minLength = minLength - self.maxLength = maxLength - self.pattern = pattern - self.allowedValues = allowedValues - self.defaultValue = defaultValue - self.example = example - } - - public func openAPISchema() -> JSONSchema { - .string( - format: format, - required: required, - nullable: nullable, - permissions: nil, - deprecated: deprecated, - title: title, - description: description, - discriminator: discriminator, - externalDocs: externalDocs, - minLength: minLength, - maxLength: maxLength, - pattern: pattern, - allowedValues: allowedValues, - defaultValue: defaultValue, - example: example - ) - } -} - -public struct ObjectSchema: SchemaRepresentable { - - public var format: JSONTypeFormat.ObjectFormat - public var required: Bool - public var nullable: Bool? - // public var permissions: Permissions? - public var deprecated: Bool? - public var title: String? - public var description: String? - public var discriminator: OpenAPI.Discriminator? - public var externalDocs: OpenAPI.ExternalDocumentation? - public var minProperties: Int? - public var maxProperties: Int? - public var properties: OrderedDictionary - // public var properties: String? - // public var additionalProperties: String? - public var allowedValues: [AnyCodable]? - public var defaultValue: AnyCodable? - public var example: AnyCodable? - - public init( - format: JSONTypeFormat.ObjectFormat = .generic, - required: Bool = false, - nullable: Bool? = nil, - // permissions: Permissions? = nil, - deprecated: Bool? = nil, - title: String? = nil, - description: String? = nil, - discriminator: OpenAPI.Discriminator? = nil, - externalDocs: OpenAPI.ExternalDocumentation? = nil, - minProperties: Int? = nil, - maxProperties: Int? = nil, - properties: OrderedDictionary, - pattern: String? = nil, - allowedValues: [AnyCodable]? = nil, - defaultValue: AnyCodable? = nil, - example: AnyCodable? = nil - ) { - self.format = format - self.required = required - self.nullable = nullable - // self.permissions = permissions - self.deprecated = deprecated - self.title = title - self.description = description - self.discriminator = discriminator - self.externalDocs = externalDocs - self.minProperties = minProperties - self.maxProperties = maxProperties - self.properties = properties - // self.pattern = pattern - self.allowedValues = allowedValues - self.defaultValue = defaultValue - self.example = example - } - - public func openAPISchema() -> JSONSchema { - .object( - format: format, - required: required, - nullable: nullable, - permissions: nil, - deprecated: deprecated, - title: title, - description: description, - discriminator: discriminator, - externalDocs: externalDocs, - minProperties: minProperties, - maxProperties: maxProperties, - properties: properties.mapValues { $0.openAPISchema() }, - // properties: [ - // "asf": .reference(.component(named: "")), - // ], - additionalProperties: nil, - allowedValues: allowedValues, - defaultValue: defaultValue, - example: example - ) - } -} diff --git a/Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift new file mode 100644 index 0000000..e44d4b5 --- /dev/null +++ b/Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift @@ -0,0 +1,45 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +protocol ArraySchemaRepresentable: + Identifiable, + OpenAPISchemaRepresentable, + SchemaPropertyRequired, + SchemaPropertyTitle, + SchemaPropertyDescription, + SchemaPropertyDeprecated, + SchemaPropertyNullable +{ + var items: JSONSchema? { get } +} + +extension ArraySchemaRepresentable { + + public func openAPISchema() -> JSONSchema { + .array( + format: .generic, + required: required, + nullable: nullable, + permissions: nil, + deprecated: deprecated, + title: title, + description: description, + discriminator: nil, + externalDocs: nil, + minItems: nil, + maxItems: nil, + uniqueItems: nil, + items: items, + allowedValues: nil, + defaultValue: nil, + example: nil + ) + } +} + diff --git a/Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift new file mode 100644 index 0000000..6478f12 --- /dev/null +++ b/Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift @@ -0,0 +1,46 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +protocol BoolSchemaRepresentable: + Identifiable, + OpenAPISchemaRepresentable, + SchemaPropertyRequired, + SchemaPropertyTitle, + SchemaPropertyDescription, + SchemaPropertyDeprecated, + SchemaPropertyNullable, + SchemaPropertyExample, + SchemaPropertyDefaultValue +where + ExamplePropertyType == Bool, + DefaultValuePropertyType == Bool +{ + +} + +extension BoolSchemaRepresentable { + + public func openAPISchema() -> JSONSchema { + .boolean( + format: .generic, + required: required, + nullable: nullable, + permissions: nil, + deprecated: deprecated, + title: title, + description: description, + discriminator: nil, + externalDocs: nil, + allowedValues: nil, + defaultValue: .init(defaultValue), + example: .init(example) + ) + } +} + diff --git a/Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift new file mode 100644 index 0000000..f31b2e6 --- /dev/null +++ b/Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift @@ -0,0 +1,50 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +protocol DoubleSchemaRepresentable: + Identifiable, + OpenAPISchemaRepresentable, + SchemaPropertyRequired, + SchemaPropertyTitle, + SchemaPropertyDescription, + SchemaPropertyDeprecated, + SchemaPropertyNullable, + SchemaPropertyExample, + SchemaPropertyDefaultValue, + SchemaPropertyAllowedValues +where + ExamplePropertyType == Double, + DefaultValuePropertyType == Double, + AllowedValuesPropertyType == Double +{ + +} + +extension DoubleSchemaRepresentable { + + public func openAPISchema() -> JSONSchema { + .number( + format: .double, + required: required, + nullable: nullable, + permissions: nil, + deprecated: deprecated, + title: title, + description: description, + discriminator: nil, + externalDocs: nil, + multipleOf: nil, + maximum: nil, + minimum: nil, + allowedValues: allowedValues?.map { .init($0) }, + defaultValue: .init(defaultValue), + example: .init(example) + ) + } +} diff --git a/Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift new file mode 100644 index 0000000..550319d --- /dev/null +++ b/Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift @@ -0,0 +1,51 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +protocol FloatSchemaRepresentable: + Identifiable, + OpenAPISchemaRepresentable, + SchemaPropertyRequired, + SchemaPropertyTitle, + SchemaPropertyDescription, + SchemaPropertyDeprecated, + SchemaPropertyNullable, + SchemaPropertyExample, + SchemaPropertyDefaultValue, + SchemaPropertyAllowedValues +where + ExamplePropertyType == Float, + DefaultValuePropertyType == Float, + AllowedValuesPropertyType == Float +{ + +} + +extension FloatSchemaRepresentable { + + public func openAPISchema() -> JSONSchema { + .number( + format: .float, + required: required, + nullable: nullable, + permissions: nil, + deprecated: deprecated, + title: title, + description: description, + discriminator: nil, + externalDocs: nil, + multipleOf: nil, + maximum: nil, + minimum: nil, + allowedValues: allowedValues?.map { .init($0) }, + defaultValue: .init(defaultValue), + example: .init(example) + ) + } +} + diff --git a/Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift new file mode 100644 index 0000000..a5700f1 --- /dev/null +++ b/Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift @@ -0,0 +1,50 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +protocol Int32SchemaRepresentable: + Identifiable, + OpenAPISchemaRepresentable, + SchemaPropertyRequired, + SchemaPropertyTitle, + SchemaPropertyDescription, + SchemaPropertyDeprecated, + SchemaPropertyNullable, + SchemaPropertyExample, + SchemaPropertyDefaultValue, + SchemaPropertyAllowedValues +where + ExamplePropertyType == Int32, + DefaultValuePropertyType == Int32, + AllowedValuesPropertyType == Int32 +{ + +} + +extension Int32SchemaRepresentable { + + public func openAPISchema() -> JSONSchema { + .integer( + format: .int32, + required: required, + nullable: nullable, + permissions: nil, + deprecated: deprecated, + title: title, + description: description, + discriminator: nil, + externalDocs: nil, + multipleOf: nil, + maximum: nil, + minimum: nil, + allowedValues: allowedValues?.map { .init($0) }, + defaultValue: .init(defaultValue), + example: .init(example) + ) + } +} diff --git a/Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift new file mode 100644 index 0000000..c97aa86 --- /dev/null +++ b/Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift @@ -0,0 +1,50 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +protocol Int64SchemaRepresentable: + Identifiable, + OpenAPISchemaRepresentable, + SchemaPropertyRequired, + SchemaPropertyTitle, + SchemaPropertyDescription, + SchemaPropertyDeprecated, + SchemaPropertyNullable, + SchemaPropertyExample, + SchemaPropertyDefaultValue, + SchemaPropertyAllowedValues +where + ExamplePropertyType == Int64, + DefaultValuePropertyType == Int64, + AllowedValuesPropertyType == Int64 +{ + +} + +extension Int64SchemaRepresentable { + + public func openAPISchema() -> JSONSchema { + .integer( + format: .int64, + required: required, + nullable: nullable, + permissions: nil, + deprecated: deprecated, + title: title, + description: description, + discriminator: nil, + externalDocs: nil, + multipleOf: nil, + maximum: nil, + minimum: nil, + allowedValues: allowedValues?.map { .init($0) }, + defaultValue: .init(defaultValue), + example: .init(example) + ) + } +} diff --git a/Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift new file mode 100644 index 0000000..2bc7c73 --- /dev/null +++ b/Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift @@ -0,0 +1,50 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +protocol IntSchemaRepresentable: + Identifiable, + OpenAPISchemaRepresentable, + SchemaPropertyRequired, + SchemaPropertyTitle, + SchemaPropertyDescription, + SchemaPropertyDeprecated, + SchemaPropertyNullable, + SchemaPropertyExample, + SchemaPropertyDefaultValue, + SchemaPropertyAllowedValues +where + ExamplePropertyType == Int, + DefaultValuePropertyType == Int, + AllowedValuesPropertyType == Int +{ + +} + +extension IntSchemaRepresentable { + + public func openAPISchema() -> JSONSchema { + .integer( + format: .unspecified, + required: required, + nullable: nullable, + permissions: nil, + deprecated: deprecated, + title: title, + description: description, + discriminator: nil, + externalDocs: nil, + multipleOf: nil, + maximum: nil, + minimum: nil, + allowedValues: allowedValues?.map { .init($0) }, + defaultValue: .init(defaultValue), + example: .init(example) + ) + } +} diff --git a/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift new file mode 100644 index 0000000..753fca0 --- /dev/null +++ b/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift @@ -0,0 +1,46 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +protocol ObjectSchemaRepresentable: + Identifiable, + OpenAPISchemaRepresentable, + SchemaPropertyRequired, + SchemaPropertyTitle, + SchemaPropertyDescription, + SchemaPropertyDeprecated, + SchemaPropertyNullable, + SchemaPropertyExample where ExamplePropertyType == AnyCodable +{ + var properties: OrderedDictionary { get } +} + +extension ObjectSchemaRepresentable { + + public func openAPISchema() -> JSONSchema { + .object( + format: .generic, + required: required, + nullable: nullable, + permissions: nil, + deprecated: deprecated, + title: title, + description: description, + discriminator: nil, + externalDocs: nil, + minProperties: nil, + maxProperties: nil, + properties: properties.mapValues { $0.openAPISchema() }, + additionalProperties: nil, + allowedValues: nil, + defaultValue: nil, + example: example + ) + } +} + diff --git a/Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift new file mode 100644 index 0000000..4210bc2 --- /dev/null +++ b/Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift @@ -0,0 +1,51 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +protocol StringSchemaRepresentable: + Identifiable, + OpenAPISchemaRepresentable, + SchemaPropertyRequired, + SchemaPropertyTitle, + SchemaPropertyDescription, + SchemaPropertyNullable, + SchemaPropertyDeprecated, + SchemaPropertyExample, + SchemaPropertyDefaultValue, + SchemaPropertyAllowedValues +where + ExamplePropertyType == String, + DefaultValuePropertyType == String, + AllowedValuesPropertyType == String +{ + +} + +extension StringSchemaRepresentable { + + public func openAPISchema() -> JSONSchema { + .string( + format: .generic, + required: required, + nullable: nullable, + permissions: nil, + deprecated: deprecated, + title: title, + description: description, + discriminator: nil, + externalDocs: nil, + minLength: nil, + maxLength: nil, + pattern: nil, + allowedValues: allowedValues?.map { .init($0) }, + defaultValue: .init(defaultValue), + example: .init(example) + ) + } +} + diff --git a/Sources/FeatherOpenAPI/Schema/_OpenAPISchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/_OpenAPISchemaRepresentable.swift new file mode 100644 index 0000000..83e6a3d --- /dev/null +++ b/Sources/FeatherOpenAPI/Schema/_OpenAPISchemaRepresentable.swift @@ -0,0 +1,20 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol OpenAPISchemaRepresentable { + + func openAPISchema() -> JSONSchema +} + +extension JSONSchema: OpenAPISchemaRepresentable { + + public func openAPISchema() -> JSONSchema { self } +} + + diff --git a/Sources/FeatherOpenAPI/Schema/_SchemaID.swift b/Sources/FeatherOpenAPI/Schema/_SchemaID.swift new file mode 100644 index 0000000..4643e0e --- /dev/null +++ b/Sources/FeatherOpenAPI/Schema/_SchemaID.swift @@ -0,0 +1,17 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +public struct SchemaID: Hashable { + + public var rawValue: String + + public init( + _ rawValue: String + ) { + self.rawValue = rawValue + } +} diff --git a/Sources/FeatherOpenAPI/Schema/_SchemaProperties.swift b/Sources/FeatherOpenAPI/Schema/_SchemaProperties.swift new file mode 100644 index 0000000..a99f9fb --- /dev/null +++ b/Sources/FeatherOpenAPI/Schema/_SchemaProperties.swift @@ -0,0 +1,96 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +// MARK: - required + +public protocol SchemaPropertyRequired { + var required: Bool { get } +} + +public extension SchemaPropertyRequired { + var required: Bool { true } +} + +// MARK: - description + +public protocol SchemaPropertyDescription { + var description: String? { get } +} + +public extension SchemaPropertyDescription { + var description: String? { nil } +} + +// MARK: - title + +public protocol SchemaPropertyTitle { + var title: String? { get } +} + +public extension SchemaPropertyTitle { + var title: String? { nil } +} + +// MARK: - deprecated + +public protocol SchemaPropertyDeprecated { + var deprecated: Bool? { get } +} + +public extension SchemaPropertyDeprecated { + var deprecated: Bool? { nil } +} + +// MARK: - nullable + +public protocol SchemaPropertyNullable { + var nullable: Bool? { get } +} + +public extension SchemaPropertyNullable { + var nullable: Bool? { nil } +} + + +// MARK: - example + +public protocol SchemaPropertyExample { + associatedtype ExamplePropertyType = AnyCodable + + var example: ExamplePropertyType? { get } +} + +public extension SchemaPropertyExample { + var example: ExamplePropertyType? { nil } +} + +// MARK: - defaultValue + +public protocol SchemaPropertyDefaultValue { + associatedtype DefaultValuePropertyType = AnyCodable + + var defaultValue: DefaultValuePropertyType? { get } +} + +public extension SchemaPropertyDefaultValue { + var defaultValue: DefaultValuePropertyType? { nil } +} + + +// MARK: - allowedValues + +public protocol SchemaPropertyAllowedValues { + associatedtype AllowedValuesPropertyType = AnyCodable + + var allowedValues: [AllowedValuesPropertyType]? { get } +} + +public extension SchemaPropertyAllowedValues { + var allowedValues: [AllowedValuesPropertyType]? { nil } +} diff --git a/Sources/FeatherOpenAPI/Schema/_SchemaReference.swift b/Sources/FeatherOpenAPI/Schema/_SchemaReference.swift new file mode 100644 index 0000000..8087ceb --- /dev/null +++ b/Sources/FeatherOpenAPI/Schema/_SchemaReference.swift @@ -0,0 +1,26 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +public struct SchemaReference: OpenAPISchemaRepresentable { + + public var id: String + public var required: Bool + + public init( + _ type: T, + required: Bool = true + ) { + self.id = type.openAPIIdentifier + self.required = required + } + + public func openAPISchema() -> JSONSchema { + .reference(.component(named: id), required: required) + } +} diff --git a/Sources/FeatherOpenAPI/SecurityRequirement.swift b/Sources/FeatherOpenAPI/SecurityRequirement.swift index 9298d4c..ecaa1f9 100644 --- a/Sources/FeatherOpenAPI/SecurityRequirement.swift +++ b/Sources/FeatherOpenAPI/SecurityRequirement.swift @@ -13,6 +13,15 @@ public protocol SecurityRequirementRepresentable { func openAPISecurityRequirement() -> OpenAPI.SecurityRequirement } +extension OpenAPI.SecurityRequirement: SecurityRequirementRepresentable { + + public func openAPISecurityRequirement() -> OpenAPI.SecurityRequirement { + self + } +} + +// MARK: - + public struct SecurityRequirement: SecurityRequirementRepresentable { public var requirements: [String: [String]] diff --git a/Sources/FeatherOpenAPI/SecurityScheme.swift b/Sources/FeatherOpenAPI/SecurityScheme.swift index 2a9f415..e27ecea 100644 --- a/Sources/FeatherOpenAPI/SecurityScheme.swift +++ b/Sources/FeatherOpenAPI/SecurityScheme.swift @@ -22,6 +22,15 @@ public protocol SecuritySchemeRepresentable { func openAPISecurityScheme() -> OpenAPI.SecurityScheme } +extension OpenAPI.SecurityScheme: SecuritySchemeRepresentable { + + public func openAPISecurityScheme() -> OpenAPI.SecurityScheme { + self + } +} + +// MARK: - + public struct SecurityScheme: SecuritySchemeRepresentable { public var type: OpenAPI.SecurityScheme.SecurityType diff --git a/Sources/FeatherOpenAPI/Server.swift b/Sources/FeatherOpenAPI/Server.swift index 2c5dfa4..0858510 100644 --- a/Sources/FeatherOpenAPI/Server.swift +++ b/Sources/FeatherOpenAPI/Server.swift @@ -11,6 +11,15 @@ public protocol ServerRepresentable { func openAPIServer() -> OpenAPI.Server } +extension OpenAPI.Server: ServerRepresentable { + + public func openAPIServer() -> OpenAPI.Server { + self + } +} + +// MARK: - + public struct Server: ServerRepresentable { public var url: URLRepresentable diff --git a/Sources/FeatherOpenAPI/Variable.swift b/Sources/FeatherOpenAPI/Variable.swift index 7c0d594..69bda79 100644 --- a/Sources/FeatherOpenAPI/Variable.swift +++ b/Sources/FeatherOpenAPI/Variable.swift @@ -11,6 +11,15 @@ public protocol ServerVariableRepresentable { func openAPIServerVariable() -> OpenAPI.Server.Variable } +extension OpenAPI.Server.Variable: ServerVariableRepresentable { + + public func openAPIServerVariable() -> OpenAPI.Server.Variable { + self + } +} + +// MARK: - + public struct ServerVariable: ServerVariableRepresentable { public var `enum`: [String] public var `default`: String diff --git a/Sources/FeatherOpenAPI/_Identifiable.swift b/Sources/FeatherOpenAPI/_Identifiable.swift new file mode 100644 index 0000000..b84096b --- /dev/null +++ b/Sources/FeatherOpenAPI/_Identifiable.swift @@ -0,0 +1,17 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +public protocol Identifiable { + var openAPIIdentifier: String { get } +} + +public extension Identifiable { + + var openAPIIdentifier: String { + String(describing: type(of: self)) + } +} diff --git a/Sources/FeatherOpenAPI/_TestObjects.swift b/Sources/FeatherOpenAPI/_TestObjects.swift new file mode 100644 index 0000000..5aaee12 --- /dev/null +++ b/Sources/FeatherOpenAPI/_TestObjects.swift @@ -0,0 +1,57 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +struct ExampleFieldId: StringSchemaRepresentable { + + var title: String? { "foo" } +} + +public struct ExampleFieldName: StringSchemaRepresentable { + + public var example: String? + + public init( + example: String? = "John Doe" + ) { + self.example = example + } +} + +struct TodoIDField: IntSchemaRepresentable { + var example: Int? { 1 } +} + +struct TodoTitleField: StringSchemaRepresentable { + var example: String? { "Buy milk" } +} + +struct TodoIsCompleteField: BoolSchemaRepresentable { +} + +struct TodoDetailObject: ObjectSchemaRepresentable { + + var properties: OrderedDictionary { + [ + "id": TodoIDField(), + "title": TodoTitleField(), + "isComplete": TodoIsCompleteField(), + "foo": SchemaReference(TodoIDField(), required: false), + ] + } +} + +struct TodoCreateRequestBody: RequestBodyRepresentable { + + var contentMap: ContentMap { + [ + : +// .json: TodoDetailObject() + ] + } +} diff --git a/Tests/FeatherOpenAPITests/Example/CreateExampleOperation.swift b/Tests/FeatherOpenAPITests/Example/CreateExampleOperation.swift index f1c05e0..9202898 100644 --- a/Tests/FeatherOpenAPITests/Example/CreateExampleOperation.swift +++ b/Tests/FeatherOpenAPITests/Example/CreateExampleOperation.swift @@ -13,7 +13,7 @@ func createExample( ) -> Operation { let testSchemaID = builder.schema(id: "TestSchema") { - StringSchema() + JSONSchema.string } let headerID = builder.header(id: "header") { @@ -25,22 +25,24 @@ func createExample( } let requestBodyID = builder.requestBody(id: "foo") { - RequestBody( + OpenAPI.Request( description: "This is a proper request body", content: [ - .json: Content(schema: testSchemaID) + : +// .json: Content(schema: testSchemaID).openAPIContent(), ] ) } let okResponseID = builder.response(id: "foo") { - Response( + OpenAPI.Response( description: "foo", headers: [ - "X-Custom-Response-Header": headerID + "X-Custom-Response-Header": .reference(.component(named: "")) ], content: [ - .aac: Content(schema: testSchemaID) + : +// .aac: Content(schema: testSchemaID) ] ) } diff --git a/Tests/FeatherOpenAPITests/Example/GetExampleOperation.swift b/Tests/FeatherOpenAPITests/Example/GetExampleOperation.swift index 7ba01ed..8c3af65 100644 --- a/Tests/FeatherOpenAPITests/Example/GetExampleOperation.swift +++ b/Tests/FeatherOpenAPITests/Example/GetExampleOperation.swift @@ -13,11 +13,11 @@ func getExample( ) -> Operation { let exampleID = builder.schema(id: "ExampleId") { - StringSchema() + JSONSchema.string } let titleSchemaID = builder.schema(id: "ExampleTitle") { - StringSchema() + JSONSchema.string } let detailSchema = builder.schema(id: "ExampleDetail") { @@ -45,13 +45,14 @@ func getExample( } let okResponse = builder.response(id: "GetExampleResponse") { - return Response( + OpenAPI.Response( description: "Example detail response", // headers: [ // "X-Custom-Response-Header": header.id // ], content: [ - .aac: Content(schema: detailSchema) + : +// .aac: Content(schema: detailSchema) ] ) } diff --git a/Tests/FeatherOpenAPITests/Example/TestOperation.swift b/Tests/FeatherOpenAPITests/Example/TestOperation.swift index 7af819e..3677ee3 100644 --- a/Tests/FeatherOpenAPITests/Example/TestOperation.swift +++ b/Tests/FeatherOpenAPITests/Example/TestOperation.swift @@ -13,7 +13,7 @@ func testOperation( ) -> Operation { let testSchemaID = builder.schema(id: "TestSchema") { - StringSchema() + JSONSchema.string } let headerID = builder.header(id: "header") { @@ -30,22 +30,24 @@ func testOperation( } let reqBody1 = builder.requestBody(id: "foo") { - RequestBody( + OpenAPI.Request( description: "This is a proper request body", content: [ - .json: Content(schema: testSchemaID) + : +// .json: Content(schema: testSchemaID).openAPIContent(), ] ) } let response1 = builder.response(id: "foo") { - Response( + OpenAPI.Response( description: "foo", headers: [ - "X-Custom-Response-Header": headerID + "X-Custom-Response-Header": .a(.component(named: "")) ], content: [ - .aac: Content(schema: testSchemaID) + : +// .aac: Content(schema: testSchemaID) ] ) } diff --git a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift index 74bc345..c4e69eb 100644 --- a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift +++ b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift @@ -17,10 +17,17 @@ import Testing @Suite struct FeatherOpenAPIKitTests { + @Test + func ttt() { + let schema = ExampleFieldId() + print(schema.openAPIIdentifier) + } + + @Test func ref() throws { - struct UserId: SchemaRepresentable { + struct UserId: OpenAPISchemaRepresentable { func openAPISchema() -> OpenAPIKit30.JSONSchema { fatalError() diff --git a/Tests/FeatherOpenAPITests/Petstore/Petstore.swift b/Tests/FeatherOpenAPITests/Petstore/Petstore.swift index 9881d93..302666c 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Petstore.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Petstore.swift @@ -41,7 +41,7 @@ func petstore( ) } - let petID = builder.schema(id: "Pet") { + let petObjectID = builder.schema(id: "Pet") { JSONSchema.object( properties: [ "id": .integer(format: .int64, example: 10), @@ -53,4 +53,76 @@ func petstore( ], ) } + + let petIdSchemaID = builder.schema(id: "petId") { + JSONSchema.integer(format: .int64, example: 10) + } + + let parameter = builder.parameter(id: "petId") { + OpenAPI.Parameter( + name: "petId", + context: .path, + schema: JSONSchema.string, + description: "ID of pet to return" + ) + } + +// let parameter = builder.parameter(id: "petId") { +// Parameter( +// name: <#T##String#>, +// context: <#T##OpenAPI.Parameter.Context#>, +// schema: <#T##SchemaID#> +// ) +// } + + + + + + let okResponseID = builder.response(id: "okResponse") { + OpenAPI.Response( + description: "successful operation", + content: [ + .json: .init(schema: .reference(.component(named: petObjectID.rawValue))), + .xml: .init(schema: .reference(.component(named: petObjectID.rawValue))), + ], + ) + } + + let operation = OpenAPI.Operation( + responses: [ + .status(code: 200): .reference(.component(named: okResponseID.rawValue)) + ] + ) } + + + +//Components { +// Schemas { +// FooSchema() +// BarSchema() +// } +// Responses { +// TodoListResponse() +// } +//} +// +//Operation { +// Id("foo") +// Description("foo br baz") +// RequestBody { +// BarSchema() +// } +// Responses { +// Status(code: 200) { +// Description("Succesful operation") +// Response(id: "foo") { +// Content(json) { +// FooSchema() +// .inline() +// } +// } +// } +// } +//} From bfae116d365c0538fbb118ad614774afe269b1e5 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Fri, 23 Jan 2026 01:55:37 +0100 Subject: [PATCH 14/34] new approach with component builders [wip] --- Sources/FeatherOpenAPI/ComponentBuilder.swift | 80 ----------- Sources/FeatherOpenAPI/Components.swift | 4 +- Sources/FeatherOpenAPI/Content/Content.swift | 9 +- .../FeatherOpenAPI/Content/ContentMap.swift | 2 +- .../Content/ContentRepresentable.swift | 9 +- Sources/FeatherOpenAPI/Document.swift | 4 +- Sources/FeatherOpenAPI/Header.swift | 65 --------- Sources/FeatherOpenAPI/Header/HeaderID.swift | 17 +++ Sources/FeatherOpenAPI/Header/HeaderMap.swift | 15 ++ .../Header/HeaderRepresentable.swift | 35 +++++ .../Header/OpenAPIHeaderRepresentable.swift | 19 +++ Sources/FeatherOpenAPI/Operation.swift | 115 --------------- .../OpenAPIOperationRepresentable.swift | 19 +++ .../Operation/OperationRepresentable.swift | 91 ++++++++++++ Sources/FeatherOpenAPI/Parameter.swift | 70 --------- .../OpenAPIParameterRepresentable.swift | 19 +++ .../Parameter/Abstraction/ParameterID.swift | 17 +++ .../Abstraction/ParameterRepresentable.swift | 35 +++++ .../CookieParameterRepresentable.swift | 22 +++ .../HeaderParameterRepresentable.swift | 22 +++ .../PathParameterRepresentable.swift | 17 +++ .../QueryParameterRepresentable.swift | 27 ++++ .../Path/OpenAPIPathItemRepresentable.swift | 19 +++ .../Path/PathCollectionRepresentable.swift | 48 +++++++ .../Path/PathItemRepresentable.swift | 78 ++++++++++ Sources/FeatherOpenAPI/Path/PathMap.swift | 14 ++ Sources/FeatherOpenAPI/PathItem.swift | 84 ----------- .../ReferencedSchemaMapRepresentable.swift | 12 ++ .../OpenAPIRequestBodyRepresentable.swift} | 0 .../RequestBodyID.swift} | 0 .../RequestBodyProperties.swift} | 0 .../RequestBodyReference.swift} | 0 .../BinaryRequestBodyRepresentable.swift | 19 ++- .../FormRequestBodyRepresentable.swift | 2 +- .../JSONRequestBodyRepresentable.swift | 2 +- .../OpenAPIResponseRepresentable.swift} | 0 .../ResponseID.swift} | 0 .../Response/Abstraction/ResponseMap.swift | 14 ++ .../ResponseRepresentable.swift} | 5 - .../BinaryResponseRepresentable.swift | 22 +++ .../Response/FormResponseRepresentable.swift | 24 ++++ .../Response/JSONResponseRepresentable.swift | 24 ++++ .../OpenAPISchemaRepresentable.swift} | 0 .../SchemaID.swift} | 0 .../Schema/Abstraction/SchemaMap.swift | 14 ++ .../SchemaProperties.swift} | 0 .../Schema/Abstraction/SchemaReference.swift | 37 +++++ .../Abstraction/SchemaRepresentable.swift | 23 +++ .../Schema/ArraySchemaRepresentable.swift | 9 +- .../Schema/BoolSchemaRepresentable.swift | 9 +- .../Schema/DoubleSchemaRepresentable.swift | 9 +- .../Schema/FloatSchemaRepresentable.swift | 9 +- .../Schema/Int32SchemaRepresentable.swift | 9 +- .../Schema/Int64SchemaRepresentable.swift | 9 +- .../Schema/IntSchemaRepresentable.swift | 9 +- .../Schema/ObjectSchemaRepresentable.swift | 30 +++- .../Schema/StringSchemaRepresentable.swift | 9 +- .../Schema/_SchemaReference.swift | 26 ---- Sources/FeatherOpenAPI/Tag.swift | 1 - Sources/FeatherOpenAPI/_Identifiable.swift | 2 +- .../Example/CreateExampleOperation.swift | 62 -------- .../Example/GetExampleOperation.swift | 71 ---------- .../Example/TestObjects.swift | 31 +++- .../Example/TestOperation.swift | 64 --------- .../FeatherOpenAPIKitTests.swift | 134 +++--------------- .../Petstore/Petstore.swift | 128 ----------------- 66 files changed, 821 insertions(+), 954 deletions(-) delete mode 100644 Sources/FeatherOpenAPI/ComponentBuilder.swift delete mode 100644 Sources/FeatherOpenAPI/Header.swift create mode 100644 Sources/FeatherOpenAPI/Header/HeaderID.swift create mode 100644 Sources/FeatherOpenAPI/Header/HeaderMap.swift create mode 100644 Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Header/OpenAPIHeaderRepresentable.swift delete mode 100644 Sources/FeatherOpenAPI/Operation.swift create mode 100644 Sources/FeatherOpenAPI/Operation/OpenAPIOperationRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift delete mode 100644 Sources/FeatherOpenAPI/Parameter.swift create mode 100644 Sources/FeatherOpenAPI/Parameter/Abstraction/OpenAPIParameterRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift create mode 100644 Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Parameter/CookieParameterRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Parameter/HeaderParameterRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Parameter/PathParameterRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Parameter/QueryParameterRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Path/OpenAPIPathItemRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Path/PathCollectionRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Path/PathItemRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Path/PathMap.swift delete mode 100644 Sources/FeatherOpenAPI/PathItem.swift create mode 100644 Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift rename Sources/FeatherOpenAPI/RequestBody/{_OpenAPIRequestBodyRepresentable.swift => Abstraction/OpenAPIRequestBodyRepresentable.swift} (100%) rename Sources/FeatherOpenAPI/RequestBody/{_RequestBodyID.swift => Abstraction/RequestBodyID.swift} (100%) rename Sources/FeatherOpenAPI/RequestBody/{_RequestBodyProperties.swift => Abstraction/RequestBodyProperties.swift} (100%) rename Sources/FeatherOpenAPI/RequestBody/{_RequestBodyReference.swift => Abstraction/RequestBodyReference.swift} (100%) rename Sources/FeatherOpenAPI/Response/{_OpenAPIResponseRepresentable.swift => Abstraction/OpenAPIResponseRepresentable.swift} (100%) rename Sources/FeatherOpenAPI/Response/{_ResponseID.swift => Abstraction/ResponseID.swift} (100%) create mode 100644 Sources/FeatherOpenAPI/Response/Abstraction/ResponseMap.swift rename Sources/FeatherOpenAPI/Response/{Response.swift => Abstraction/ResponseRepresentable.swift} (88%) create mode 100644 Sources/FeatherOpenAPI/Response/BinaryResponseRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Response/FormResponseRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Response/JSONResponseRepresentable.swift rename Sources/FeatherOpenAPI/Schema/{_OpenAPISchemaRepresentable.swift => Abstraction/OpenAPISchemaRepresentable.swift} (100%) rename Sources/FeatherOpenAPI/Schema/{_SchemaID.swift => Abstraction/SchemaID.swift} (100%) create mode 100644 Sources/FeatherOpenAPI/Schema/Abstraction/SchemaMap.swift rename Sources/FeatherOpenAPI/Schema/{_SchemaProperties.swift => Abstraction/SchemaProperties.swift} (100%) create mode 100644 Sources/FeatherOpenAPI/Schema/Abstraction/SchemaReference.swift create mode 100644 Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift delete mode 100644 Sources/FeatherOpenAPI/Schema/_SchemaReference.swift delete mode 100644 Tests/FeatherOpenAPITests/Example/CreateExampleOperation.swift delete mode 100644 Tests/FeatherOpenAPITests/Example/GetExampleOperation.swift rename Sources/FeatherOpenAPI/_TestObjects.swift => Tests/FeatherOpenAPITests/Example/TestObjects.swift (60%) delete mode 100644 Tests/FeatherOpenAPITests/Example/TestOperation.swift delete mode 100644 Tests/FeatherOpenAPITests/Petstore/Petstore.swift diff --git a/Sources/FeatherOpenAPI/ComponentBuilder.swift b/Sources/FeatherOpenAPI/ComponentBuilder.swift deleted file mode 100644 index 49c4580..0000000 --- a/Sources/FeatherOpenAPI/ComponentBuilder.swift +++ /dev/null @@ -1,80 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public struct ComponentBuilder { - - public var components: Components - - public init( - components: Components = .init() - ) { - self.components = components - } - - public mutating func header( - id: String, - builder: (() -> HeaderRepresentable) - ) -> HeaderID { - let id = HeaderID(id) - let header = builder() - - components.headers[id] = header - - return id - } - - public mutating func parameter( - id: String, - builder: (() -> OpenAPI.Parameter) - ) -> ParameterID { - let id = ParameterID(id) - let parameter = builder() - - components.parameters[id] = parameter - - return id - } - - public mutating func schema( - id: String, - builder: (() -> OpenAPISchemaRepresentable) - ) -> SchemaID { - let id = SchemaID(id) - let schema = builder() - - components.schemas[id] = schema - - return id - } - - public mutating func response( - id: String, - builder: (() -> OpenAPIResponseRepresentable) - ) -> ResponseID { - let id = ResponseID(id) - let response = builder() - - components.responses[id] = response - - return id - } - - public mutating func requestBody( - id: String, - builder: (() -> OpenAPIRequestBodyRepresentable) - ) -> RequestBodyID { - let id = RequestBodyID(id) - let requestBody = builder() - - components.requestBodies[id] = requestBody - - return id - } - -} diff --git a/Sources/FeatherOpenAPI/Components.swift b/Sources/FeatherOpenAPI/Components.swift index 77572e4..6d17415 100644 --- a/Sources/FeatherOpenAPI/Components.swift +++ b/Sources/FeatherOpenAPI/Components.swift @@ -30,7 +30,7 @@ public struct Components: ComponentsRepresentable { public var responses: OrderedDictionary public var requestBodies: OrderedDictionary - public var headers: OrderedDictionary + public var headers: OrderedDictionary public var securitySchemes: OrderedDictionary public var links: OrderedDictionary @@ -45,7 +45,7 @@ public struct Components: ComponentsRepresentable { requestBodies: OrderedDictionary< RequestBodyID, OpenAPIRequestBodyRepresentable > = [:], - headers: OrderedDictionary = [:], + headers: OrderedDictionary = [:], securitySchemes: OrderedDictionary< SecuritySchemeID, SecuritySchemeRepresentable > = [:], diff --git a/Sources/FeatherOpenAPI/Content/Content.swift b/Sources/FeatherOpenAPI/Content/Content.swift index b61fae0..90735bf 100644 --- a/Sources/FeatherOpenAPI/Content/Content.swift +++ b/Sources/FeatherOpenAPI/Content/Content.swift @@ -5,10 +5,12 @@ // Created by Tibor Bödecs on 2026. 01. 23.. // +import OpenAPIKit30 -public struct Content: ContentRepresentable { - - public var schema: any OpenAPISchemaRepresentable { +public struct Content: + ContentRepresentable +{ + public var schema: any SchemaRepresentable { _schema } @@ -17,5 +19,4 @@ public struct Content: ContentRepresentable { public init(_ schema: T) { self._schema = schema } - } diff --git a/Sources/FeatherOpenAPI/Content/ContentMap.swift b/Sources/FeatherOpenAPI/Content/ContentMap.swift index 711439b..c07709d 100644 --- a/Sources/FeatherOpenAPI/Content/ContentMap.swift +++ b/Sources/FeatherOpenAPI/Content/ContentMap.swift @@ -10,5 +10,5 @@ import OpenAPIKit30 public typealias ContentMap = OrderedDictionary < OpenAPI.ContentType, - OpenAPIContentRepresentable + ContentRepresentable > diff --git a/Sources/FeatherOpenAPI/Content/ContentRepresentable.swift b/Sources/FeatherOpenAPI/Content/ContentRepresentable.swift index dda981c..38947ef 100644 --- a/Sources/FeatherOpenAPI/Content/ContentRepresentable.swift +++ b/Sources/FeatherOpenAPI/Content/ContentRepresentable.swift @@ -8,9 +8,10 @@ import OpenAPIKit30 public protocol ContentRepresentable: - OpenAPIContentRepresentable + OpenAPIContentRepresentable, + ReferencedSchemaMapRepresentable { - var schema: OpenAPISchemaRepresentable { get } + var schema: SchemaRepresentable { get } } public extension ContentRepresentable { @@ -23,4 +24,8 @@ public extension ContentRepresentable { vendorExtensions: [:] ) } + + var referencedSchemaMap: OrderedDictionary { + schema.referencedSchemaMap + } } diff --git a/Sources/FeatherOpenAPI/Document.swift b/Sources/FeatherOpenAPI/Document.swift index 9c7f2f2..7826df4 100644 --- a/Sources/FeatherOpenAPI/Document.swift +++ b/Sources/FeatherOpenAPI/Document.swift @@ -24,7 +24,7 @@ public struct Document: DocumentRepresentable { public var info: InfoRepresentable public var servers: [ServerRepresentable] - public var paths: OrderedDictionary + public var paths: OrderedDictionary public var components: ComponentsRepresentable public var security: [SecurityRequirementRepresentable] public var externalDocumentation: ExternalDocumentationRepresentable? @@ -33,7 +33,7 @@ public struct Document: DocumentRepresentable { public init( info: InfoRepresentable, servers: [ServerRepresentable] = [], - paths: OrderedDictionary, + paths: OrderedDictionary, components: ComponentsRepresentable, security: [SecurityRequirementRepresentable] = [], externalDocumentation: ExternalDocumentationRepresentable? = nil, diff --git a/Sources/FeatherOpenAPI/Header.swift b/Sources/FeatherOpenAPI/Header.swift deleted file mode 100644 index ff03ac3..0000000 --- a/Sources/FeatherOpenAPI/Header.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public struct HeaderID: Hashable { - - public var rawValue: String - - public init( - _ rawValue: String - ) { - self.rawValue = rawValue - } -} - -public protocol HeaderRepresentable { - func openAPIHeader() -> OpenAPI.Header -} - -extension OpenAPI.Header: HeaderRepresentable { - - public func openAPIHeader() -> OpenAPI.Header { - self - } -} - -// MARK: - - -public struct Header: HeaderRepresentable { - - public var schema: SchemaID - public var description: String? - public var required: Bool - public var deprecated: Bool - public var vendorExtensions: [String: AnyCodable] - - public init( - schema: SchemaID, - description: String? = nil, - required: Bool = false, - deprecated: Bool = false, - vendorExtensions: [String: AnyCodable] = [:] - ) { - self.schema = schema - self.description = description - self.required = required - self.deprecated = deprecated - self.vendorExtensions = vendorExtensions - } - - public func openAPIHeader() -> OpenAPI.Header { - .init( - schemaReference: .component(named: schema.rawValue), - description: description, - required: required, - deprecated: deprecated, - vendorExtensions: vendorExtensions - ) - } -} diff --git a/Sources/FeatherOpenAPI/Header/HeaderID.swift b/Sources/FeatherOpenAPI/Header/HeaderID.swift new file mode 100644 index 0000000..1fcbdb1 --- /dev/null +++ b/Sources/FeatherOpenAPI/Header/HeaderID.swift @@ -0,0 +1,17 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +public struct HeaderID: Hashable { + + public var rawValue: String + + public init( + _ rawValue: String + ) { + self.rawValue = rawValue + } +} diff --git a/Sources/FeatherOpenAPI/Header/HeaderMap.swift b/Sources/FeatherOpenAPI/Header/HeaderMap.swift new file mode 100644 index 0000000..1fe03b6 --- /dev/null +++ b/Sources/FeatherOpenAPI/Header/HeaderMap.swift @@ -0,0 +1,15 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public typealias HeaderMap = OrderedDictionary +< + String, + OpenAPIHeaderRepresentable +> + diff --git a/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift b/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift new file mode 100644 index 0000000..f3245e9 --- /dev/null +++ b/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift @@ -0,0 +1,35 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol HeaderRepresentable: + OpenAPIHeaderRepresentable +{ + var schema: OpenAPISchemaRepresentable { get } + + var description: String? { get } + var required: Bool { get } + var deprecated: Bool { get } +} + +public extension HeaderRepresentable { + + var description: String? { nil } + var required: Bool { false } + var deprecated: Bool { false } + + func openAPIHeader() -> OpenAPI.Header { + .init( + schema: schema.openAPISchema(), + description: description, + required: required, + deprecated: deprecated, + vendorExtensions: [:] + ) + } +} diff --git a/Sources/FeatherOpenAPI/Header/OpenAPIHeaderRepresentable.swift b/Sources/FeatherOpenAPI/Header/OpenAPIHeaderRepresentable.swift new file mode 100644 index 0000000..7e787d0 --- /dev/null +++ b/Sources/FeatherOpenAPI/Header/OpenAPIHeaderRepresentable.swift @@ -0,0 +1,19 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol OpenAPIHeaderRepresentable { + func openAPIHeader() -> OpenAPI.Header +} + +extension OpenAPI.Header: OpenAPIHeaderRepresentable { + + public func openAPIHeader() -> OpenAPI.Header { + self + } +} diff --git a/Sources/FeatherOpenAPI/Operation.swift b/Sources/FeatherOpenAPI/Operation.swift deleted file mode 100644 index f53648e..0000000 --- a/Sources/FeatherOpenAPI/Operation.swift +++ /dev/null @@ -1,115 +0,0 @@ -// -// File 2.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public protocol OperationRepresentable { - func openAPIOperation() -> OpenAPI.Operation -} - - -extension OpenAPI.Operation: OperationRepresentable { - - public func openAPIOperation() -> OpenAPI.Operation { - self - } -} - -// MARK: - - -public struct Operation: OperationRepresentable { - - public var tags: [String]? - public var summary: String? - public var description: String? - public var externalDocs: ExternalDocumentationRepresentable? - public var operationId: String? - - public var parameters: [ParameterID] - public var requestBody: RequestBodyID? - public var responses: - OrderedDictionary - - public var deprecated: Bool - public var security: [SecurityRequirementRepresentable]? - public var servers: [ServerRepresentable]? - public var vendorExtensions: [String: AnyCodable] - - public init( - id: String? = nil, - tags: [String]? = nil, - summary: String? = nil, - description: String? = nil, - externalDocs: ExternalDocumentationRepresentable? = nil, - parameters: [ParameterID] = [], - requestBody: RequestBodyID? = nil, - responses: OrderedDictionary, - deprecated: Bool = false, - security: [SecurityRequirementRepresentable]? = nil, - servers: [ServerRepresentable]? = nil, - vendorExtensions: [String: AnyCodable] = [:] - ) { - self.tags = tags - self.summary = summary - self.description = description - self.externalDocs = externalDocs - self.operationId = id - self.parameters = parameters - self.requestBody = requestBody - self.responses = responses - self.deprecated = deprecated - self.security = security - self.servers = servers - self.vendorExtensions = vendorExtensions - } - - public func openAPIOperation() -> OpenAPI.Operation { - if let requestBody { - return .init( - tags: tags, - summary: summary, - description: description, - externalDocs: externalDocs?.openAPIExternalDocumentation(), - operationId: operationId, - parameters: parameters.map { - .reference(.component(named: $0.rawValue)) - }, - requestBody: .reference( - .component( - named: requestBody.rawValue - ) - ), - responses: responses.mapValues { - .reference(.component(named: $0.rawValue)) - }, - callbacks: .init(), - deprecated: deprecated, - security: security?.map { $0.openAPISecurityRequirement() }, - servers: servers?.map { $0.openAPIServer() }, - vendorExtensions: vendorExtensions - ) - } - return .init( - tags: tags, - summary: summary, - description: description, - externalDocs: externalDocs?.openAPIExternalDocumentation(), - operationId: operationId, - parameters: parameters.map { - .reference(.component(named: $0.rawValue)) - }, - responses: responses.mapValues { - .reference(.component(named: $0.rawValue)) - }, - callbacks: .init(), - deprecated: deprecated, - security: security?.map { $0.openAPISecurityRequirement() }, - servers: servers?.map { $0.openAPIServer() }, - vendorExtensions: vendorExtensions - ) - } -} diff --git a/Sources/FeatherOpenAPI/Operation/OpenAPIOperationRepresentable.swift b/Sources/FeatherOpenAPI/Operation/OpenAPIOperationRepresentable.swift new file mode 100644 index 0000000..dd7f789 --- /dev/null +++ b/Sources/FeatherOpenAPI/Operation/OpenAPIOperationRepresentable.swift @@ -0,0 +1,19 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol OpenAPIOperationRepresentable { + func openAPIOperation() -> OpenAPI.Operation +} + +extension OpenAPI.Operation: OpenAPIOperationRepresentable { + + public func openAPIOperation() -> OpenAPI.Operation { + self + } +} diff --git a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift new file mode 100644 index 0000000..002c2ca --- /dev/null +++ b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift @@ -0,0 +1,91 @@ +// +// File 2.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + + +public protocol OperationRepresentable: + OpenAPIOperationRepresentable, + ReferencedSchemaMapRepresentable +{ +// associatedtype RequestBodyType: RequestBodyRepresentable + +// var tags: [String]? + var summary: String? { get } + var description: String? { get } + var operationId: String? { get } + + var parameters: [ParameterRepresentable] { get } + var requestBody: RequestBodyRepresentable? { get } + var responseMap: ResponseMap { get } + + var deprecated: Bool { get } +// var security: [SecurityRequirementRepresentable]? +// var servers: [ServerRepresentable]? +} + +public extension OperationRepresentable { + + var summary: String? { nil } + var description: String? { nil } + var operationId: String? { nil } + var parameters: [ParameterRepresentable] { [] } + + var requestBody: RequestBodyRepresentable? { nil } + + var deprecated: Bool { false } + + func openAPIOperation() -> OpenAPI.Operation { + .init( + tags: nil, + summary: summary, + description: description, + externalDocs: nil, + operationId: operationId, + parameters: parameters.map { .init($0.openAPIParameter()) }, + requestBody: requestBody?.openAPIRequestBody(), + responses: responseMap.mapValues { .init($0.openAPIResponse()) }, + callbacks: [:], + deprecated: deprecated, + security: nil, + servers: nil, + vendorExtensions: [:] + ) + } + + var referencedSchemaMap: OrderedDictionary { + var results = OrderedDictionary() + + for parameter in parameters { + if let ref = parameter.schema as? SchemaReferenceRepresentable { + results[ref.id] = ref.object + } + } + + for content in requestBody?.contentMap.values ?? [] { + if let ref = content.schema as? SchemaReferenceRepresentable { + results[ref.id] = ref.object + } + } + + let headers = responseMap.values + .map { $0.headerMap.values } + .flatMap { $0 } + + let contents = responseMap.values + .map { $0.contentMap.values } + .flatMap { $0 } + + for content in contents { + for (k, v) in content.referencedSchemaMap { + results[k] = v + } + } + + return results + } +} diff --git a/Sources/FeatherOpenAPI/Parameter.swift b/Sources/FeatherOpenAPI/Parameter.swift deleted file mode 100644 index 64f7f18..0000000 --- a/Sources/FeatherOpenAPI/Parameter.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public struct ParameterID: Hashable { - - public var rawValue: String - - public init( - _ rawValue: String - ) { - self.rawValue = rawValue - } -} - -public protocol ParameterRepresentable { - func openAPIParameter() -> OpenAPI.Parameter -} - -extension OpenAPI.Parameter: ParameterRepresentable { - - public func openAPIParameter() -> OpenAPI.Parameter { - self - } -} - -// MARK: - - - -public struct Parameter: ParameterRepresentable { - - public var name: String - public var context: OpenAPI.Parameter.Context - public var schema: SchemaID - public var description: String? - public var deprecated: Bool - public var vendorExtensions: [String: AnyCodable] - - public init( - name: String, - context: OpenAPI.Parameter.Context, - schema: SchemaID, - description: String? = nil, - deprecated: Bool = false, - vendorExtensions: [String: AnyCodable] = [:] - ) { - self.name = name - self.context = context - self.schema = schema - self.description = description - self.deprecated = deprecated - self.vendorExtensions = vendorExtensions - } - - public func openAPIParameter() -> OpenAPI.Parameter { - .init( - name: name, - context: context, - schema: .reference(.component(named: schema.rawValue), required: true), - description: description, - deprecated: deprecated, - vendorExtensions: vendorExtensions - ) - } -} diff --git a/Sources/FeatherOpenAPI/Parameter/Abstraction/OpenAPIParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/Abstraction/OpenAPIParameterRepresentable.swift new file mode 100644 index 0000000..95ea1a0 --- /dev/null +++ b/Sources/FeatherOpenAPI/Parameter/Abstraction/OpenAPIParameterRepresentable.swift @@ -0,0 +1,19 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol OpenAPIParameterRepresentable { + func openAPIParameter() -> OpenAPI.Parameter +} + +extension OpenAPI.Parameter: OpenAPIParameterRepresentable { + + public func openAPIParameter() -> OpenAPI.Parameter { + self + } +} diff --git a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift new file mode 100644 index 0000000..3bb5245 --- /dev/null +++ b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift @@ -0,0 +1,17 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +public struct ParameterID: Hashable { + + public var rawValue: String + + public init( + _ rawValue: String + ) { + self.rawValue = rawValue + } +} diff --git a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift new file mode 100644 index 0000000..d30433f --- /dev/null +++ b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift @@ -0,0 +1,35 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol ParameterRepresentable: + OpenAPIParameterRepresentable +{ + var name: String { get } + var context: OpenAPI.Parameter.Context { get } + var schema: OpenAPISchemaRepresentable { get } + var description: String? { get } + var deprecated: Bool { get } +} + +public extension ParameterRepresentable { + + var description: String? { nil } + var deprecated: Bool { false } + + func openAPIParameter() -> OpenAPI.Parameter { + .init( + name: name, + context: context, + schema: schema.openAPISchema(), + description: description, + deprecated: deprecated, + vendorExtensions: [:] + ) + } +} diff --git a/Sources/FeatherOpenAPI/Parameter/CookieParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/CookieParameterRepresentable.swift new file mode 100644 index 0000000..71b6b48 --- /dev/null +++ b/Sources/FeatherOpenAPI/Parameter/CookieParameterRepresentable.swift @@ -0,0 +1,22 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol CookieParameterRepresentable: ParameterRepresentable { + + var required: Bool { get } +} + +public extension CookieParameterRepresentable { + + var required: Bool { false } + + var context: OpenAPI.Parameter.Context { + .cookie(required: required) + } +} diff --git a/Sources/FeatherOpenAPI/Parameter/HeaderParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/HeaderParameterRepresentable.swift new file mode 100644 index 0000000..a55f08d --- /dev/null +++ b/Sources/FeatherOpenAPI/Parameter/HeaderParameterRepresentable.swift @@ -0,0 +1,22 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol HeaderParameterRepresentable: ParameterRepresentable { + + var required: Bool { get } +} + +public extension HeaderParameterRepresentable { + + var required: Bool { false } + + var context: OpenAPI.Parameter.Context { + .header(required: required) + } +} diff --git a/Sources/FeatherOpenAPI/Parameter/PathParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/PathParameterRepresentable.swift new file mode 100644 index 0000000..bb1d9f3 --- /dev/null +++ b/Sources/FeatherOpenAPI/Parameter/PathParameterRepresentable.swift @@ -0,0 +1,17 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol PathParameterRepresentable: ParameterRepresentable { + +} + +public extension PathParameterRepresentable { + + var context: OpenAPI.Parameter.Context { .path } +} diff --git a/Sources/FeatherOpenAPI/Parameter/QueryParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/QueryParameterRepresentable.swift new file mode 100644 index 0000000..2e5d5a6 --- /dev/null +++ b/Sources/FeatherOpenAPI/Parameter/QueryParameterRepresentable.swift @@ -0,0 +1,27 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol QueryParameterRepresentable: ParameterRepresentable { + + var required: Bool { get } + var allowEmptyValue: Bool { get } +} + +public extension QueryParameterRepresentable { + + var required: Bool { false } + var allowEmptyValue: Bool { true } + + var context: OpenAPI.Parameter.Context { + .query( + required: required, + allowEmptyValue: allowEmptyValue + ) + } +} diff --git a/Sources/FeatherOpenAPI/Path/OpenAPIPathItemRepresentable.swift b/Sources/FeatherOpenAPI/Path/OpenAPIPathItemRepresentable.swift new file mode 100644 index 0000000..1d29cba --- /dev/null +++ b/Sources/FeatherOpenAPI/Path/OpenAPIPathItemRepresentable.swift @@ -0,0 +1,19 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol OpenAPIPathItemRepresentable { + func openAPIPathItem() -> OpenAPI.PathItem +} + +extension OpenAPI.PathItem: OpenAPIPathItemRepresentable { + + public func openAPIPathItem() -> OpenAPI.PathItem { + self + } +} diff --git a/Sources/FeatherOpenAPI/Path/PathCollectionRepresentable.swift b/Sources/FeatherOpenAPI/Path/PathCollectionRepresentable.swift new file mode 100644 index 0000000..49542eb --- /dev/null +++ b/Sources/FeatherOpenAPI/Path/PathCollectionRepresentable.swift @@ -0,0 +1,48 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol PathCollectionRepresentable: + ReferencedSchemaMapRepresentable +{ + var pathMap: PathMap { get } + var components: Components { get } +} + +extension PathCollectionRepresentable { + + var referencedSchemaMap: OrderedDictionary { + var results = OrderedDictionary() + + let schemaMaps = pathMap.values + .map { $0.referencedSchemaMap } + .flatMap { $0 } + + for (k, v) in schemaMaps { + results[k] = v + } + return results + } + + var components: Components { + + + + return .init( + schemas: referencedSchemaMap.mapValues { $0.openAPISchema() }, + parameters: .init(), + examples: .init(), + responses: .init(), + requestBodies: .init(), + headers: .init(), + securitySchemes: .init(), + links: .init(), + vendorExtensions: .init() + ) + } +} diff --git a/Sources/FeatherOpenAPI/Path/PathItemRepresentable.swift b/Sources/FeatherOpenAPI/Path/PathItemRepresentable.swift new file mode 100644 index 0000000..0bf57ba --- /dev/null +++ b/Sources/FeatherOpenAPI/Path/PathItemRepresentable.swift @@ -0,0 +1,78 @@ +// +// File 2.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol PathItemRepresentable: + OpenAPIPathItemRepresentable, + ReferencedSchemaMapRepresentable +{ + var summary: String? { get } + var description: String? { get } + var servers: [ServerRepresentable]? { get } + var get: OperationRepresentable? { get } + var put: OperationRepresentable? { get } + var post: OperationRepresentable? { get } + var delete: OperationRepresentable? { get } + var options: OperationRepresentable? { get } + var head: OperationRepresentable? { get } + var patch: OperationRepresentable? { get } + var trace: OperationRepresentable? { get } +} + +public extension PathItemRepresentable { + + var summary: String? { nil } + var description: String? { nil } + var servers: [ServerRepresentable]? { nil } + var get: OperationRepresentable? { nil } + var put: OperationRepresentable? { nil } + var post: OperationRepresentable? { nil } + var delete: OperationRepresentable? { nil } + var options: OperationRepresentable? { nil } + var head: OperationRepresentable? { nil } + var patch: OperationRepresentable? { nil } + var trace: OperationRepresentable? { nil } + + func openAPIPathItem() -> OpenAPI.PathItem { + .init( + summary: summary, + description: description, + servers: servers?.map { $0.openAPIServer() }, + parameters: [], + get: get?.openAPIOperation(), + put: put?.openAPIOperation(), + post: post?.openAPIOperation(), + delete: delete?.openAPIOperation(), + options: options?.openAPIOperation(), + head: head?.openAPIOperation(), + patch: patch?.openAPIOperation(), + trace: trace?.openAPIOperation(), + vendorExtensions: [:] + ) + } + + var referencedSchemaMap: OrderedDictionary { + var results = OrderedDictionary() + + let schemaMaps = [ + get, + put, + post, + delete, + options, + head, + patch, + trace + ].compactMap { $0 }.map { $0.referencedSchemaMap }.flatMap { $0 } + + for (k, v) in schemaMaps { + results[k] = v + } + return results + } +} diff --git a/Sources/FeatherOpenAPI/Path/PathMap.swift b/Sources/FeatherOpenAPI/Path/PathMap.swift new file mode 100644 index 0000000..335f4ff --- /dev/null +++ b/Sources/FeatherOpenAPI/Path/PathMap.swift @@ -0,0 +1,14 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public typealias PathMap = OrderedDictionary +< + OpenAPI.Path, + PathItemRepresentable +> diff --git a/Sources/FeatherOpenAPI/PathItem.swift b/Sources/FeatherOpenAPI/PathItem.swift deleted file mode 100644 index 94c37a2..0000000 --- a/Sources/FeatherOpenAPI/PathItem.swift +++ /dev/null @@ -1,84 +0,0 @@ -// -// File 2.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public protocol PathItemRepresentable { - func openAPIPathItem() -> OpenAPI.PathItem -} - -extension OpenAPI.PathItem: PathItemRepresentable { - - public func openAPIPathItem() -> OpenAPI.PathItem { - self - } -} - -// MARK: - - - -public struct PathItem: PathItemRepresentable { - - public var summary: String? - public var description: String? - public var servers: [ServerRepresentable]? - public var get: OperationRepresentable? - public var put: OperationRepresentable? - public var post: OperationRepresentable? - public var delete: OperationRepresentable? - public var options: OperationRepresentable? - public var head: OperationRepresentable? - public var patch: OperationRepresentable? - public var trace: OperationRepresentable? - public var vendorExtensions: [String: AnyCodable] - - public init( - summary: String? = nil, - description: String? = nil, - servers: [ServerRepresentable]? = nil, - get: OperationRepresentable? = nil, - put: OperationRepresentable? = nil, - post: OperationRepresentable? = nil, - delete: OperationRepresentable? = nil, - options: OperationRepresentable? = nil, - head: OperationRepresentable? = nil, - patch: OperationRepresentable? = nil, - trace: OperationRepresentable? = nil, - vendorExtensions: [String: AnyCodable] = [:] - ) { - self.summary = summary - self.description = description - self.servers = servers - self.get = get - self.put = put - self.post = post - self.delete = delete - self.options = options - self.head = head - self.patch = patch - self.trace = trace - self.vendorExtensions = vendorExtensions - } - - public func openAPIPathItem() -> OpenAPI.PathItem { - .init( - summary: summary, - description: description, - servers: servers?.map { $0.openAPIServer() }, - parameters: [], - get: get?.openAPIOperation(), - put: put?.openAPIOperation(), - post: post?.openAPIOperation(), - delete: delete?.openAPIOperation(), - options: options?.openAPIOperation(), - head: head?.openAPIOperation(), - patch: patch?.openAPIOperation(), - trace: trace?.openAPIOperation(), - vendorExtensions: vendorExtensions - ) - } -} diff --git a/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift b/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift new file mode 100644 index 0000000..e94edf9 --- /dev/null +++ b/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift @@ -0,0 +1,12 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol ReferencedSchemaMapRepresentable { + var referencedSchemaMap: OrderedDictionary { get } +} diff --git a/Sources/FeatherOpenAPI/RequestBody/_OpenAPIRequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/Abstraction/OpenAPIRequestBodyRepresentable.swift similarity index 100% rename from Sources/FeatherOpenAPI/RequestBody/_OpenAPIRequestBodyRepresentable.swift rename to Sources/FeatherOpenAPI/RequestBody/Abstraction/OpenAPIRequestBodyRepresentable.swift diff --git a/Sources/FeatherOpenAPI/RequestBody/_RequestBodyID.swift b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyID.swift similarity index 100% rename from Sources/FeatherOpenAPI/RequestBody/_RequestBodyID.swift rename to Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyID.swift diff --git a/Sources/FeatherOpenAPI/RequestBody/_RequestBodyProperties.swift b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyProperties.swift similarity index 100% rename from Sources/FeatherOpenAPI/RequestBody/_RequestBodyProperties.swift rename to Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyProperties.swift diff --git a/Sources/FeatherOpenAPI/RequestBody/_RequestBodyReference.swift b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyReference.swift similarity index 100% rename from Sources/FeatherOpenAPI/RequestBody/_RequestBodyReference.swift rename to Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyReference.swift diff --git a/Sources/FeatherOpenAPI/RequestBody/BinaryRequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/BinaryRequestBodyRepresentable.swift index 7c16e4a..4665bf8 100644 --- a/Sources/FeatherOpenAPI/RequestBody/BinaryRequestBodyRepresentable.swift +++ b/Sources/FeatherOpenAPI/RequestBody/BinaryRequestBodyRepresentable.swift @@ -15,12 +15,21 @@ public extension BinaryRequestBodyRepresentable { var contentMap: ContentMap { [ - .other("application/octet-stream"): Content( - JSONSchema.string( - format: .binary - ) - ) + .other("application/octet-stream"): Content(BinarySchema()) ] } } +#warning("move this") +struct BinarySchema: SchemaRepresentable { + + func openAPISchema() -> JSONSchema { + JSONSchema.string( + format: .binary + ) + } + + var referencedSchemaMap: OrderedDictionary { + [:] + } +} diff --git a/Sources/FeatherOpenAPI/RequestBody/FormRequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/FormRequestBodyRepresentable.swift index 46e1332..b2d0e5d 100644 --- a/Sources/FeatherOpenAPI/RequestBody/FormRequestBodyRepresentable.swift +++ b/Sources/FeatherOpenAPI/RequestBody/FormRequestBodyRepresentable.swift @@ -8,7 +8,7 @@ import OpenAPIKit30 public protocol FormRequestBodyRepresentable: RequestBodyRepresentable { - associatedtype SchemaType: OpenAPISchemaRepresentable + associatedtype SchemaType: SchemaRepresentable var schema: SchemaType { get } } diff --git a/Sources/FeatherOpenAPI/RequestBody/JSONRequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/JSONRequestBodyRepresentable.swift index 68de6c0..5c4c91d 100644 --- a/Sources/FeatherOpenAPI/RequestBody/JSONRequestBodyRepresentable.swift +++ b/Sources/FeatherOpenAPI/RequestBody/JSONRequestBodyRepresentable.swift @@ -8,7 +8,7 @@ import OpenAPIKit30 public protocol JSONRequestBodyRepresentable: RequestBodyRepresentable { - associatedtype SchemaType: OpenAPISchemaRepresentable + associatedtype SchemaType: SchemaRepresentable var schema: SchemaType { get } } diff --git a/Sources/FeatherOpenAPI/Response/_OpenAPIResponseRepresentable.swift b/Sources/FeatherOpenAPI/Response/Abstraction/OpenAPIResponseRepresentable.swift similarity index 100% rename from Sources/FeatherOpenAPI/Response/_OpenAPIResponseRepresentable.swift rename to Sources/FeatherOpenAPI/Response/Abstraction/OpenAPIResponseRepresentable.swift diff --git a/Sources/FeatherOpenAPI/Response/_ResponseID.swift b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseID.swift similarity index 100% rename from Sources/FeatherOpenAPI/Response/_ResponseID.swift rename to Sources/FeatherOpenAPI/Response/Abstraction/ResponseID.swift diff --git a/Sources/FeatherOpenAPI/Response/Abstraction/ResponseMap.swift b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseMap.swift new file mode 100644 index 0000000..f78b664 --- /dev/null +++ b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseMap.swift @@ -0,0 +1,14 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public typealias ResponseMap = OrderedDictionary +< + OpenAPI.Response.StatusCode, + ResponseRepresentable +> diff --git a/Sources/FeatherOpenAPI/Response/Response.swift b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseRepresentable.swift similarity index 88% rename from Sources/FeatherOpenAPI/Response/Response.swift rename to Sources/FeatherOpenAPI/Response/Abstraction/ResponseRepresentable.swift index 3fc0266..5e43009 100644 --- a/Sources/FeatherOpenAPI/Response/Response.swift +++ b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseRepresentable.swift @@ -7,11 +7,6 @@ import OpenAPIKit30 -public typealias HeaderMap = OrderedDictionary< - String, - HeaderRepresentable -> - public protocol ResponseRepresentable: OpenAPIResponseRepresentable { diff --git a/Sources/FeatherOpenAPI/Response/BinaryResponseRepresentable.swift b/Sources/FeatherOpenAPI/Response/BinaryResponseRepresentable.swift new file mode 100644 index 0000000..2e66ea0 --- /dev/null +++ b/Sources/FeatherOpenAPI/Response/BinaryResponseRepresentable.swift @@ -0,0 +1,22 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol BinaryResponseRepresentable: ResponseRepresentable { + +} + +public extension BinaryResponseRepresentable { + + var contentMap: ContentMap { + [ + .other("application/octet-stream"): Content(BinarySchema()) + ] + } +} + diff --git a/Sources/FeatherOpenAPI/Response/FormResponseRepresentable.swift b/Sources/FeatherOpenAPI/Response/FormResponseRepresentable.swift new file mode 100644 index 0000000..66d5399 --- /dev/null +++ b/Sources/FeatherOpenAPI/Response/FormResponseRepresentable.swift @@ -0,0 +1,24 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol FormResponseRepresentable: ResponseRepresentable { + associatedtype SchemaType: SchemaRepresentable + + var schema: SchemaType { get } +} + +public extension FormResponseRepresentable { + + var contentMap: ContentMap { + [ + .form: Content(schema) + ] + } +} + diff --git a/Sources/FeatherOpenAPI/Response/JSONResponseRepresentable.swift b/Sources/FeatherOpenAPI/Response/JSONResponseRepresentable.swift new file mode 100644 index 0000000..6303779 --- /dev/null +++ b/Sources/FeatherOpenAPI/Response/JSONResponseRepresentable.swift @@ -0,0 +1,24 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol JSONResponseRepresentable: ResponseRepresentable { + associatedtype SchemaType: SchemaRepresentable + + var schema: SchemaType { get } +} + +public extension JSONResponseRepresentable { + + var contentMap: ContentMap { + [ + .json: Content(schema) + ] + } +} + diff --git a/Sources/FeatherOpenAPI/Schema/_OpenAPISchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/OpenAPISchemaRepresentable.swift similarity index 100% rename from Sources/FeatherOpenAPI/Schema/_OpenAPISchemaRepresentable.swift rename to Sources/FeatherOpenAPI/Schema/Abstraction/OpenAPISchemaRepresentable.swift diff --git a/Sources/FeatherOpenAPI/Schema/_SchemaID.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaID.swift similarity index 100% rename from Sources/FeatherOpenAPI/Schema/_SchemaID.swift rename to Sources/FeatherOpenAPI/Schema/Abstraction/SchemaID.swift diff --git a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaMap.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaMap.swift new file mode 100644 index 0000000..55d62a8 --- /dev/null +++ b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaMap.swift @@ -0,0 +1,14 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public typealias SchemaMap = OrderedDictionary +< + String, + OpenAPISchemaRepresentable +> diff --git a/Sources/FeatherOpenAPI/Schema/_SchemaProperties.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaProperties.swift similarity index 100% rename from Sources/FeatherOpenAPI/Schema/_SchemaProperties.swift rename to Sources/FeatherOpenAPI/Schema/Abstraction/SchemaProperties.swift diff --git a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaReference.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaReference.swift new file mode 100644 index 0000000..3b333c0 --- /dev/null +++ b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaReference.swift @@ -0,0 +1,37 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +public protocol SchemaReferenceRepresentable { + var id: SchemaID { get } + var object: SchemaRepresentable { get } +} + +public struct SchemaReference: OpenAPISchemaRepresentable, SchemaReferenceRepresentable { + + public var object: any SchemaRepresentable { + _object + } + + public var id: SchemaID + public var _object: T + public var required: Bool + + public init( + _ object: T, + required: Bool = true + ) { + self.id = .init(object.openAPIIdentifier) + self._object = object + self.required = required + } + + public func openAPISchema() -> JSONSchema { + .reference(.component(named: id.rawValue), required: required) + } +} diff --git a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift new file mode 100644 index 0000000..41fd41d --- /dev/null +++ b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift @@ -0,0 +1,23 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol SchemaRepresentable: + OpenAPISchemaRepresentable, + Identifiable, + ReferencedSchemaMapRepresentable +{ + +} + +extension SchemaRepresentable { + + public var referencedSchemaMap: OrderedDictionary { + [:] + } +} diff --git a/Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift index e44d4b5..79e39e2 100644 --- a/Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift @@ -7,9 +7,8 @@ import OpenAPIKit30 -protocol ArraySchemaRepresentable: - Identifiable, - OpenAPISchemaRepresentable, +public protocol ArraySchemaRepresentable: + SchemaRepresentable, SchemaPropertyRequired, SchemaPropertyTitle, SchemaPropertyDescription, @@ -19,9 +18,9 @@ protocol ArraySchemaRepresentable: var items: JSONSchema? { get } } -extension ArraySchemaRepresentable { +public extension ArraySchemaRepresentable { - public func openAPISchema() -> JSONSchema { + func openAPISchema() -> JSONSchema { .array( format: .generic, required: required, diff --git a/Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift index 6478f12..fbf21eb 100644 --- a/Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift @@ -7,9 +7,8 @@ import OpenAPIKit30 -protocol BoolSchemaRepresentable: - Identifiable, - OpenAPISchemaRepresentable, +public protocol BoolSchemaRepresentable: + SchemaRepresentable, SchemaPropertyRequired, SchemaPropertyTitle, SchemaPropertyDescription, @@ -24,9 +23,9 @@ where } -extension BoolSchemaRepresentable { +public extension BoolSchemaRepresentable { - public func openAPISchema() -> JSONSchema { + func openAPISchema() -> JSONSchema { .boolean( format: .generic, required: required, diff --git a/Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift index f31b2e6..88c1417 100644 --- a/Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift @@ -7,9 +7,8 @@ import OpenAPIKit30 -protocol DoubleSchemaRepresentable: - Identifiable, - OpenAPISchemaRepresentable, +public protocol DoubleSchemaRepresentable: + SchemaRepresentable, SchemaPropertyRequired, SchemaPropertyTitle, SchemaPropertyDescription, @@ -26,9 +25,9 @@ where } -extension DoubleSchemaRepresentable { +public extension DoubleSchemaRepresentable { - public func openAPISchema() -> JSONSchema { + func openAPISchema() -> JSONSchema { .number( format: .double, required: required, diff --git a/Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift index 550319d..2f143af 100644 --- a/Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift @@ -7,9 +7,8 @@ import OpenAPIKit30 -protocol FloatSchemaRepresentable: - Identifiable, - OpenAPISchemaRepresentable, +public protocol FloatSchemaRepresentable: + SchemaRepresentable, SchemaPropertyRequired, SchemaPropertyTitle, SchemaPropertyDescription, @@ -26,9 +25,9 @@ where } -extension FloatSchemaRepresentable { +public extension FloatSchemaRepresentable { - public func openAPISchema() -> JSONSchema { + func openAPISchema() -> JSONSchema { .number( format: .float, required: required, diff --git a/Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift index a5700f1..00be643 100644 --- a/Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift @@ -7,9 +7,8 @@ import OpenAPIKit30 -protocol Int32SchemaRepresentable: - Identifiable, - OpenAPISchemaRepresentable, +public protocol Int32SchemaRepresentable: + SchemaRepresentable, SchemaPropertyRequired, SchemaPropertyTitle, SchemaPropertyDescription, @@ -26,9 +25,9 @@ where } -extension Int32SchemaRepresentable { +public extension Int32SchemaRepresentable { - public func openAPISchema() -> JSONSchema { + func openAPISchema() -> JSONSchema { .integer( format: .int32, required: required, diff --git a/Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift index c97aa86..041a91a 100644 --- a/Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift @@ -7,9 +7,8 @@ import OpenAPIKit30 -protocol Int64SchemaRepresentable: - Identifiable, - OpenAPISchemaRepresentable, +public protocol Int64SchemaRepresentable: + SchemaRepresentable, SchemaPropertyRequired, SchemaPropertyTitle, SchemaPropertyDescription, @@ -26,9 +25,9 @@ where } -extension Int64SchemaRepresentable { +public extension Int64SchemaRepresentable { - public func openAPISchema() -> JSONSchema { + func openAPISchema() -> JSONSchema { .integer( format: .int64, required: required, diff --git a/Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift index 2bc7c73..165e509 100644 --- a/Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift @@ -7,9 +7,8 @@ import OpenAPIKit30 -protocol IntSchemaRepresentable: - Identifiable, - OpenAPISchemaRepresentable, +public protocol IntSchemaRepresentable: + SchemaRepresentable, SchemaPropertyRequired, SchemaPropertyTitle, SchemaPropertyDescription, @@ -26,9 +25,9 @@ where } -extension IntSchemaRepresentable { +public extension IntSchemaRepresentable { - public func openAPISchema() -> JSONSchema { + func openAPISchema() -> JSONSchema { .integer( format: .unspecified, required: required, diff --git a/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift index 753fca0..d93f447 100644 --- a/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift @@ -7,9 +7,8 @@ import OpenAPIKit30 -protocol ObjectSchemaRepresentable: - Identifiable, - OpenAPISchemaRepresentable, +public protocol ObjectSchemaRepresentable: + SchemaRepresentable, SchemaPropertyRequired, SchemaPropertyTitle, SchemaPropertyDescription, @@ -17,12 +16,12 @@ protocol ObjectSchemaRepresentable: SchemaPropertyNullable, SchemaPropertyExample where ExamplePropertyType == AnyCodable { - var properties: OrderedDictionary { get } + var propertyMap: SchemaMap { get } } -extension ObjectSchemaRepresentable { +public extension ObjectSchemaRepresentable { - public func openAPISchema() -> JSONSchema { + func openAPISchema() -> JSONSchema { .object( format: .generic, required: required, @@ -35,12 +34,29 @@ extension ObjectSchemaRepresentable { externalDocs: nil, minProperties: nil, maxProperties: nil, - properties: properties.mapValues { $0.openAPISchema() }, + properties: propertyMap.mapValues { $0.openAPISchema() }, additionalProperties: nil, allowedValues: nil, defaultValue: nil, example: example ) } + + var referencedSchemaMap: OrderedDictionary { + var results = OrderedDictionary() +// +// for property in propertyMap.values { +// if let ref = property as? SchemaReferenceRepresentable { +// results[ref.id] = ref.object +// } +// } + + for (_, value) in propertyMap { + if let ref = value as? SchemaReferenceRepresentable { + results[ref.id] = ref.object + } + } + return results + } } diff --git a/Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift index 4210bc2..0cd278c 100644 --- a/Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift @@ -7,9 +7,8 @@ import OpenAPIKit30 -protocol StringSchemaRepresentable: - Identifiable, - OpenAPISchemaRepresentable, +public protocol StringSchemaRepresentable: + SchemaRepresentable, SchemaPropertyRequired, SchemaPropertyTitle, SchemaPropertyDescription, @@ -26,9 +25,9 @@ where } -extension StringSchemaRepresentable { +public extension StringSchemaRepresentable { - public func openAPISchema() -> JSONSchema { + func openAPISchema() -> JSONSchema { .string( format: .generic, required: required, diff --git a/Sources/FeatherOpenAPI/Schema/_SchemaReference.swift b/Sources/FeatherOpenAPI/Schema/_SchemaReference.swift deleted file mode 100644 index 8087ceb..0000000 --- a/Sources/FeatherOpenAPI/Schema/_SchemaReference.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 22.. -// - -import OpenAPIKit30 - -public struct SchemaReference: OpenAPISchemaRepresentable { - - public var id: String - public var required: Bool - - public init( - _ type: T, - required: Bool = true - ) { - self.id = type.openAPIIdentifier - self.required = required - } - - public func openAPISchema() -> JSONSchema { - .reference(.component(named: id), required: required) - } -} diff --git a/Sources/FeatherOpenAPI/Tag.swift b/Sources/FeatherOpenAPI/Tag.swift index d1d5159..4472b7a 100644 --- a/Sources/FeatherOpenAPI/Tag.swift +++ b/Sources/FeatherOpenAPI/Tag.swift @@ -5,4 +5,3 @@ // Created by Tibor Bödecs on 2026. 01. 21.. // -import Foundation diff --git a/Sources/FeatherOpenAPI/_Identifiable.swift b/Sources/FeatherOpenAPI/_Identifiable.swift index b84096b..1adeab2 100644 --- a/Sources/FeatherOpenAPI/_Identifiable.swift +++ b/Sources/FeatherOpenAPI/_Identifiable.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -public protocol Identifiable { +public protocol Identifiable: Sendable { var openAPIIdentifier: String { get } } diff --git a/Tests/FeatherOpenAPITests/Example/CreateExampleOperation.swift b/Tests/FeatherOpenAPITests/Example/CreateExampleOperation.swift deleted file mode 100644 index 9202898..0000000 --- a/Tests/FeatherOpenAPITests/Example/CreateExampleOperation.swift +++ /dev/null @@ -1,62 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import FeatherOpenAPI -import OpenAPIKit30 - -func createExample( - using builder: inout ComponentBuilder -) -> Operation { - - let testSchemaID = builder.schema(id: "TestSchema") { - JSONSchema.string - } - - let headerID = builder.header(id: "header") { - Header(schema: testSchemaID) - } - - let p1 = builder.parameter(id: "foo") { - .init(name: "foo", context: .path, schema: .string) - } - - let requestBodyID = builder.requestBody(id: "foo") { - OpenAPI.Request( - description: "This is a proper request body", - content: [ - : -// .json: Content(schema: testSchemaID).openAPIContent(), - ] - ) - } - - let okResponseID = builder.response(id: "foo") { - OpenAPI.Response( - description: "foo", - headers: [ - "X-Custom-Response-Header": .reference(.component(named: "")) - ], - content: [ - : -// .aac: Content(schema: testSchemaID) - ] - ) - } - - return .init( - tags: ["main"], - summary: "Detail example", - description: "Detail example detail", - parameters: [ - p1 - ], - requestBody: requestBodyID, - responses: [ - 200: okResponseID - ] - ) -} diff --git a/Tests/FeatherOpenAPITests/Example/GetExampleOperation.swift b/Tests/FeatherOpenAPITests/Example/GetExampleOperation.swift deleted file mode 100644 index 8c3af65..0000000 --- a/Tests/FeatherOpenAPITests/Example/GetExampleOperation.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import FeatherOpenAPI -import OpenAPIKit30 - -func getExample( - using builder: inout ComponentBuilder -) -> Operation { - - let exampleID = builder.schema(id: "ExampleId") { - JSONSchema.string - } - - let titleSchemaID = builder.schema(id: "ExampleTitle") { - JSONSchema.string - } - - let detailSchema = builder.schema(id: "ExampleDetail") { - return JSONSchema.object( - properties: [ - "id": .reference(.component(named: exampleID.rawValue)), - "title": .reference(.component(named: titleSchemaID.rawValue)), - ] - ) - // ObjectSchema( - // properties: [ - // "id": StringSchema(), - // "title": StringSchema(), - // ] - // ) - } - - let idParameter = builder.parameter(id: "ExampleId") { - Parameter( - name: "id", - context: .path, - schema: exampleID - ) - .openAPIParameter() - } - - let okResponse = builder.response(id: "GetExampleResponse") { - OpenAPI.Response( - description: "Example detail response", - // headers: [ - // "X-Custom-Response-Header": header.id - // ], - content: [ - : -// .aac: Content(schema: detailSchema) - ] - ) - } - - return .init( - tags: ["main"], - summary: "Detail example", - description: "Detail example detail", - parameters: [ - idParameter - ], - responses: [ - 200: okResponse - ] - ) -} diff --git a/Sources/FeatherOpenAPI/_TestObjects.swift b/Tests/FeatherOpenAPITests/Example/TestObjects.swift similarity index 60% rename from Sources/FeatherOpenAPI/_TestObjects.swift rename to Tests/FeatherOpenAPITests/Example/TestObjects.swift index 5aaee12..1691023 100644 --- a/Sources/FeatherOpenAPI/_TestObjects.swift +++ b/Tests/FeatherOpenAPITests/Example/TestObjects.swift @@ -2,11 +2,13 @@ // File.swift // feather-openapi // -// Created by Tibor Bödecs on 2026. 01. 22.. +// Created by Tibor Bödecs on 2026. 01. 21.. // +import FeatherOpenAPI import OpenAPIKit30 + struct ExampleFieldId: StringSchemaRepresentable { var title: String? { "foo" } @@ -32,18 +34,22 @@ struct TodoTitleField: StringSchemaRepresentable { } struct TodoIsCompleteField: BoolSchemaRepresentable { + } struct TodoDetailObject: ObjectSchemaRepresentable { - var properties: OrderedDictionary { + var propertyMap: SchemaMap { [ "id": TodoIDField(), "title": TodoTitleField(), "isComplete": TodoIsCompleteField(), + // TODO: move required to schema -> use that to reference "foo": SchemaReference(TodoIDField(), required: false), +// "foo": TodoIDField().reference(id: "lorem"), ] } + } struct TodoCreateRequestBody: RequestBodyRepresentable { @@ -55,3 +61,24 @@ struct TodoCreateRequestBody: RequestBodyRepresentable { ] } } + +struct TodoCreateResponse: JSONResponseRepresentable { + var description: String = "Todo response" + var schema = TodoDetailObject() +} + + +struct TodoCreateOperation: OperationRepresentable { + + var requestBody = TodoCreateRequestBody() + var responseMap: ResponseMap { + [ + 200: TodoCreateResponse(), + ] + } +} + + +struct TodoPathItems: PathItemRepresentable { + var post: OperationRepresentable? = TodoCreateOperation() +} diff --git a/Tests/FeatherOpenAPITests/Example/TestOperation.swift b/Tests/FeatherOpenAPITests/Example/TestOperation.swift deleted file mode 100644 index 3677ee3..0000000 --- a/Tests/FeatherOpenAPITests/Example/TestOperation.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import FeatherOpenAPI -import OpenAPIKit30 - -func testOperation( - using builder: inout ComponentBuilder -) -> Operation { - - let testSchemaID = builder.schema(id: "TestSchema") { - JSONSchema.string - } - - let headerID = builder.header(id: "header") { - Header(schema: testSchemaID) - } - - let p1 = builder.parameter(id: "foo") { - Parameter( - name: "foo", - context: .path, - schema: testSchemaID - ) - .openAPIParameter() - } - - let reqBody1 = builder.requestBody(id: "foo") { - OpenAPI.Request( - description: "This is a proper request body", - content: [ - : -// .json: Content(schema: testSchemaID).openAPIContent(), - ] - ) - } - - let response1 = builder.response(id: "foo") { - OpenAPI.Response( - description: "foo", - headers: [ - "X-Custom-Response-Header": .a(.component(named: "")) - ], - content: [ - : -// .aac: Content(schema: testSchemaID) - ] - ) - } - - return .init( - parameters: [ - p1 - ], - requestBody: reqBody1, - responses: [ - 200: response1 - ] - ) -} diff --git a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift index c4e69eb..5bddeb2 100644 --- a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift +++ b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift @@ -18,142 +18,46 @@ import Testing struct FeatherOpenAPIKitTests { @Test - func ttt() { - let schema = ExampleFieldId() - print(schema.openAPIIdentifier) - } - - - @Test - func ref() throws { - - struct UserId: OpenAPISchemaRepresentable { - - func openAPISchema() -> OpenAPIKit30.JSONSchema { - fatalError() - } - } - - struct Reference { + func example() throws { - static func toSchemaRef() { - print(T.self) + struct MyPathCollection: PathCollectionRepresentable { + + var pathMap: PathMap { + [ + "todos": TodoPathItems() + ] } } - - Reference.self.toSchemaRef() - } - - @Test - func example() throws { - - var builder = ComponentBuilder() - - let getExampleOperation = getExample(using: &builder) - // let createExampleOperation = createExample(using: &builder) - - let doc = Document( + + let collection = MyPathCollection() + + let document = Document( info: Info( title: "foo", version: "1.0.0" ), - paths: [ - "examples": PathItem( - summary: "Example related operations", - get: getExampleOperation, - // post: createExampleOperation - ) - ], - components: builder.components + paths: collection.pathMap.mapValues { $0.openAPIPathItem() }, + components: collection.components ) - let openAPIdoc = doc.openAPIDocument() + let openAPIdoc = document.openAPIDocument() let encoder = YAMLEncoder() - do { - _ = - try openAPIdoc - .locallyDereferenced() - .resolved() - } - catch { - print(error) - throw error - } + _ = try openAPIdoc + .locallyDereferenced() + .resolved() let result = try encoder.encode(openAPIdoc) - print(result) - - } - - func renderTest() throws { - - let doc = OpenAPIKit30.OpenAPI.Document( - info: .init( - title: "foo", - version: "3.0.0" - ), - servers: [], - paths: [ - "foo": .init( - .init( - summary: "foo", - get: .init( - requestBody: .init( - .component( - named: "foo" - ) - ), - responses: [:] - ), - ) - ) - ], - components: .init( - schemas: [ - "schemaID": .string( - format: .dateTime, - example: "Foo" - ) - ], - requestBodies: [ - "foo": .init( - description: "foo", - content: [ - .json: .init( - schemaReference: .component(named: "schemaID") - ) - ] - ) - ], - ) - ) - - let encoder = YAMLEncoder() - - do { - _ = - try doc - .locallyDereferenced() - .resolved() - } - catch { - print(type(of: error)) - print(error) - throw error - } - - let result = try encoder.encode(doc) print("---- 3.0 ----") print(result) - let doc31 = doc.convert(to: .v3_1_0) + let doc31 = openAPIdoc.convert(to: .v3_1_0) let result31 = try encoder.encode(doc31) print("---- 3.1 ----") print(result31) - let doc32 = doc.convert(to: .v3_2_0) + let doc32 = openAPIdoc.convert(to: .v3_2_0) let result32 = try encoder.encode(doc32) print("---- 3.2 ----") print(result32) diff --git a/Tests/FeatherOpenAPITests/Petstore/Petstore.swift b/Tests/FeatherOpenAPITests/Petstore/Petstore.swift deleted file mode 100644 index 302666c..0000000 --- a/Tests/FeatherOpenAPITests/Petstore/Petstore.swift +++ /dev/null @@ -1,128 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 22.. -// - -import FeatherOpenAPI -import OpenAPIKit30 - -func petstore( - using builder: inout ComponentBuilder -) { - - let apiResponseID = builder.schema(id: "ApiResponse") { - JSONSchema.object( - properties: [ - "code": .integer(format: .int32), - "type": .string, - "message": .string, - ], - ) - } - - let tagID = builder.schema(id: "Tag") { - JSONSchema.object( - properties: [ - "id": .integer(format: .int64), - "name": .string, - ], - ) - } - - let categoryID = builder.schema(id: "Category") { - JSONSchema.object( - properties: [ - "code": .integer(format: .int32), - "type": .string, - "message": .string, - ], - ) - } - - let petObjectID = builder.schema(id: "Pet") { - JSONSchema.object( - properties: [ - "id": .integer(format: .int64, example: 10), - "name": .string(example: "doggie"), - "category": .reference( - .component(named: categoryID.rawValue), - required: true - ), - ], - ) - } - - let petIdSchemaID = builder.schema(id: "petId") { - JSONSchema.integer(format: .int64, example: 10) - } - - let parameter = builder.parameter(id: "petId") { - OpenAPI.Parameter( - name: "petId", - context: .path, - schema: JSONSchema.string, - description: "ID of pet to return" - ) - } - -// let parameter = builder.parameter(id: "petId") { -// Parameter( -// name: <#T##String#>, -// context: <#T##OpenAPI.Parameter.Context#>, -// schema: <#T##SchemaID#> -// ) -// } - - - - - - let okResponseID = builder.response(id: "okResponse") { - OpenAPI.Response( - description: "successful operation", - content: [ - .json: .init(schema: .reference(.component(named: petObjectID.rawValue))), - .xml: .init(schema: .reference(.component(named: petObjectID.rawValue))), - ], - ) - } - - let operation = OpenAPI.Operation( - responses: [ - .status(code: 200): .reference(.component(named: okResponseID.rawValue)) - ] - ) -} - - - -//Components { -// Schemas { -// FooSchema() -// BarSchema() -// } -// Responses { -// TodoListResponse() -// } -//} -// -//Operation { -// Id("foo") -// Description("foo br baz") -// RequestBody { -// BarSchema() -// } -// Responses { -// Status(code: 200) { -// Description("Succesful operation") -// Response(id: "foo") { -// Content(json) { -// FooSchema() -// .inline() -// } -// } -// } -// } -//} From 4dd7fdd645f40edee72c45f73b4ad7d2010b855b Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Fri, 23 Jan 2026 16:48:36 +0100 Subject: [PATCH 15/34] improvements --- Package.resolved | 11 +- Package.swift | 63 +------- Sources/FeatherOpenAPI/Callback.swift | 39 ----- .../FeatherOpenAPI/Callback/CallbackID.swift | 17 +++ .../Components/Components.swift | 40 ++++++ .../ComponentsRepresentable.swift} | 112 ++++++--------- .../OpenAPIComponentsRepresentable.swift | 20 +++ Sources/FeatherOpenAPI/Contact.swift | 39 ----- .../Contact/ContactRepresentable.swift | 33 +++++ .../Contact/OpenAPIContactRepresentable.swift | 20 +++ .../Content/ContentRepresentable.swift | 5 +- Sources/FeatherOpenAPI/Document.swift | 64 --------- .../Document/DocumentRepresentable.swift | 43 ++++++ .../OpenAPIDocumentRepresentable.swift | 19 +++ Sources/FeatherOpenAPI/Example.swift | 62 -------- .../FeatherOpenAPI/Example/ExampleID.swift | 17 +++ .../Example/ExampleRepresentable.swift | 29 ++++ .../Example/OpenAPIExampleRepresentable.swift | 19 +++ .../ExternalDocsRepresentable.swift | 28 ++++ .../OpenAPIExternalDocsRepresentable.swift | 20 +++ .../ExternalDocumentation.swift | 52 ------- .../Header/HeaderRepresentable.swift | 18 +-- Sources/FeatherOpenAPI/Identifiable.swift | 28 ++++ Sources/FeatherOpenAPI/Info.swift | 63 -------- .../Info/InfoRepresentable.swift | 39 +++++ .../Info/OpenAPIInfoRepresentable.swift | 20 +++ Sources/FeatherOpenAPI/License.swift | 37 ----- .../License/LicenseRepresentable.swift | 27 ++++ .../License/OpenAPILicenseRepresentable.swift | 19 +++ Sources/FeatherOpenAPI/Link/LinkID.swift | 17 +++ .../Link/OpenAPILinkRepresentable.swift | 19 +++ Sources/FeatherOpenAPI/Links.swift | 47 ------ Sources/FeatherOpenAPI/Location.swift | 40 ------ .../Location/LocationRepresentable.swift | 28 ++++ .../OpenAPILocationRepresentable.swift | 23 +++ .../Operation/OperationRepresentable.swift | 13 +- .../Abstraction/ParameterRepresentable.swift | 13 +- .../CookieParameterRepresentable.swift | 12 +- .../HeaderParameterRepresentable.swift | 10 +- .../QueryParameterRepresentable.swift | 10 +- .../PathCollectionRepresentable.swift | 13 +- .../OpenAPIPathItemRepresentable.swift | 0 .../PathItemRepresentable.swift | 14 +- .../{Path => PathItem}/PathMap.swift | 0 .../Abstraction/RequestBodyProperties.swift | 27 ---- .../RequestBodyRepresentable.swift | 9 +- .../Abstraction/ResponseRepresentable.swift | 5 +- .../Schema/Abstraction/SchemaProperties.swift | 96 ------------- .../Schema/Abstraction/SchemaReference.swift | 6 +- .../Abstraction/SchemaRepresentable.swift | 15 +- .../Schema/ArraySchemaRepresentable.swift | 9 +- .../Schema/BoolSchemaRepresentable.swift | 11 +- .../Schema/DoubleSchemaRepresentable.swift | 13 +- .../Schema/FloatSchemaRepresentable.swift | 13 +- .../Schema/Int32SchemaRepresentable.swift | 13 +- .../Schema/Int64SchemaRepresentable.swift | 13 +- .../Schema/IntSchemaRepresentable.swift | 13 +- .../Schema/ObjectSchemaRepresentable.swift | 11 +- .../Schema/StringSchemaRepresentable.swift | 13 +- .../FeatherOpenAPI/SecurityRequirement.swift | 43 ------ ...nAPISecurityRequirementRepresentable.swift | 21 +++ .../SecurityRequirementRepresentable.swift | 25 ++++ Sources/FeatherOpenAPI/SecurityScheme.swift | 47 ------ .../OpenAPISecuritySchemeRepresentable.swift | 19 +++ .../SecurityScheme/SecuritySchemeID.swift | 17 +++ .../SecuritySchemeRepresentable.swift | 27 ++++ Sources/FeatherOpenAPI/Server.swift | 40 ------ .../Server/OpenAPIServerRepresentable.swift | 19 +++ .../Server/ServerRepresentable.swift | 36 +++++ Sources/FeatherOpenAPI/Tag.swift | 7 - .../Tag/OpenAPITagRepresentable.swift | 19 +++ .../FeatherOpenAPI/Tag/TagRepresentable.swift | 33 +++++ Sources/FeatherOpenAPI/Variable.swift | 37 ----- .../OpenAPIVariableRepresentable.swift | 19 +++ .../FeatherOpenAPI/Variable/VariableMap.swift | 13 ++ .../Variable/VariableRepresentable.swift | 29 ++++ Sources/FeatherOpenAPI/_Identifiable.swift | 17 --- .../_Properties/AllowedValuesProperty.swift | 18 +++ .../_Properties/DefaultValueProperty.swift | 18 +++ .../_Properties/DeprecatedProperty.swift | 14 ++ .../_Properties/DescriptionProperty.swift | 14 ++ .../_Properties/ExampleProperty.swift | 18 +++ .../_Properties/NullableProperty.swift | 15 ++ .../_Properties/RequiredProperty.swift | 14 ++ .../_Properties/TitleProperty.swift | 14 ++ .../VendorExtensionsProperty.swift | 18 +++ .../Entrypoint.swift | 71 --------- .../TypeCollector.swift | 136 ------------------ .../Example/TestObjects.swift | 26 +++- .../FeatherOpenAPIKitTests.swift | 53 ++++--- 90 files changed, 1152 insertions(+), 1274 deletions(-) delete mode 100644 Sources/FeatherOpenAPI/Callback.swift create mode 100644 Sources/FeatherOpenAPI/Callback/CallbackID.swift create mode 100644 Sources/FeatherOpenAPI/Components/Components.swift rename Sources/FeatherOpenAPI/{Components.swift => Components/ComponentsRepresentable.swift} (58%) create mode 100644 Sources/FeatherOpenAPI/Components/OpenAPIComponentsRepresentable.swift delete mode 100644 Sources/FeatherOpenAPI/Contact.swift create mode 100644 Sources/FeatherOpenAPI/Contact/ContactRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Contact/OpenAPIContactRepresentable.swift delete mode 100644 Sources/FeatherOpenAPI/Document.swift create mode 100644 Sources/FeatherOpenAPI/Document/DocumentRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Document/OpenAPIDocumentRepresentable.swift delete mode 100644 Sources/FeatherOpenAPI/Example.swift create mode 100644 Sources/FeatherOpenAPI/Example/ExampleID.swift create mode 100644 Sources/FeatherOpenAPI/Example/ExampleRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Example/OpenAPIExampleRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/ExternalDocs/ExternalDocsRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/ExternalDocs/OpenAPIExternalDocsRepresentable.swift delete mode 100644 Sources/FeatherOpenAPI/ExternalDocumentation.swift create mode 100644 Sources/FeatherOpenAPI/Identifiable.swift delete mode 100644 Sources/FeatherOpenAPI/Info.swift create mode 100644 Sources/FeatherOpenAPI/Info/InfoRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Info/OpenAPIInfoRepresentable.swift delete mode 100644 Sources/FeatherOpenAPI/License.swift create mode 100644 Sources/FeatherOpenAPI/License/LicenseRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/License/OpenAPILicenseRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Link/LinkID.swift create mode 100644 Sources/FeatherOpenAPI/Link/OpenAPILinkRepresentable.swift delete mode 100644 Sources/FeatherOpenAPI/Links.swift delete mode 100644 Sources/FeatherOpenAPI/Location.swift create mode 100644 Sources/FeatherOpenAPI/Location/LocationRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Location/OpenAPILocationRepresentable.swift rename Sources/FeatherOpenAPI/{Path => PathCollection}/PathCollectionRepresentable.swift (72%) rename Sources/FeatherOpenAPI/{Path => PathItem}/OpenAPIPathItemRepresentable.swift (100%) rename Sources/FeatherOpenAPI/{Path => PathItem}/PathItemRepresentable.swift (89%) rename Sources/FeatherOpenAPI/{Path => PathItem}/PathMap.swift (100%) delete mode 100644 Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyProperties.swift rename Sources/FeatherOpenAPI/RequestBody/{ => Abstraction}/RequestBodyRepresentable.swift (75%) delete mode 100644 Sources/FeatherOpenAPI/Schema/Abstraction/SchemaProperties.swift delete mode 100644 Sources/FeatherOpenAPI/SecurityRequirement.swift create mode 100644 Sources/FeatherOpenAPI/SecurityRequirement/OpenAPISecurityRequirementRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/SecurityRequirement/SecurityRequirementRepresentable.swift delete mode 100644 Sources/FeatherOpenAPI/SecurityScheme.swift create mode 100644 Sources/FeatherOpenAPI/SecurityScheme/OpenAPISecuritySchemeRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeID.swift create mode 100644 Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeRepresentable.swift delete mode 100644 Sources/FeatherOpenAPI/Server.swift create mode 100644 Sources/FeatherOpenAPI/Server/OpenAPIServerRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Server/ServerRepresentable.swift delete mode 100644 Sources/FeatherOpenAPI/Tag.swift create mode 100644 Sources/FeatherOpenAPI/Tag/OpenAPITagRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Tag/TagRepresentable.swift delete mode 100644 Sources/FeatherOpenAPI/Variable.swift create mode 100644 Sources/FeatherOpenAPI/Variable/OpenAPIVariableRepresentable.swift create mode 100644 Sources/FeatherOpenAPI/Variable/VariableMap.swift create mode 100644 Sources/FeatherOpenAPI/Variable/VariableRepresentable.swift delete mode 100644 Sources/FeatherOpenAPI/_Identifiable.swift create mode 100644 Sources/FeatherOpenAPI/_Properties/AllowedValuesProperty.swift create mode 100644 Sources/FeatherOpenAPI/_Properties/DefaultValueProperty.swift create mode 100644 Sources/FeatherOpenAPI/_Properties/DeprecatedProperty.swift create mode 100644 Sources/FeatherOpenAPI/_Properties/DescriptionProperty.swift create mode 100644 Sources/FeatherOpenAPI/_Properties/ExampleProperty.swift create mode 100644 Sources/FeatherOpenAPI/_Properties/NullableProperty.swift create mode 100644 Sources/FeatherOpenAPI/_Properties/RequiredProperty.swift create mode 100644 Sources/FeatherOpenAPI/_Properties/TitleProperty.swift create mode 100644 Sources/FeatherOpenAPI/_Properties/VendorExtensionsProperty.swift delete mode 100644 Sources/feather-openapikit-generator/Entrypoint.swift delete mode 100644 Sources/feather-openapikit-generator/TypeCollector.swift diff --git a/Package.resolved b/Package.resolved index 83587b1..cff3abd 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "8f98dd02ffc2297204019bd52be53aa9500ecde4ef10fb829fd78a13b4b83f8f", + "originHash" : "00017e3ffac1735a2e9564fd12a07204929f5647630eb11ad464a2d3aca034bc", "pins" : [ { "identity" : "openapikit", @@ -10,15 +10,6 @@ "version" : "5.0.0-rc.2" } }, - { - "identity" : "swift-syntax", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-syntax.git", - "state" : { - "revision" : "4799286537280063c85a32f09884cfbca301b1a1", - "version" : "602.0.0" - } - }, { "identity" : "yams", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index 041bf93..2c987c9 100644 --- a/Package.swift +++ b/Package.swift @@ -1,8 +1,6 @@ // swift-tools-version:6.1 import PackageDescription -import CompilerPluginSupport - // NOTE: https://github.com/swift-server/swift-http-server/blob/main/Package.swift var defaultSwiftSettings: [SwiftSetting] = [ @@ -33,48 +31,13 @@ let package = Package( .visionOS(.v2), ], products: [ - .executable( - name: "feather-openapikit-generator", - targets: ["feather-openapikit-generator"] - ), - .library( - name: "FeatherOpenAPI", - targets: ["FeatherOpenAPI"] - ), - .plugin( - name: "FeatherOpenAPIKitGenerator", - targets: ["FeatherOpenAPIKitGenerator"] - ), + .library(name: "FeatherOpenAPI", targets: ["FeatherOpenAPI"]), ], dependencies: [ .package(url: "https://github.com/mattpolzin/OpenAPIKit", exact: "5.0.0-rc.2"), - .package(url: "https://github.com/apple/swift-syntax", exact: "602.0.0"), .package(url: "https://github.com/jpsim/Yams", from: "6.2.0"), ], targets: [ - .plugin( - name: "FeatherOpenAPIKitGenerator", - capability: .buildTool(), - dependencies: [ - .target(name: "feather-openapikit-generator") - ], - ), - // MARK: - - .executableTarget( - name: "feather-openapikit-generator", - dependencies: [ - .product(name: "SwiftParser", package: "swift-syntax") - ], - swiftSettings: defaultSwiftSettings, - ), - .target( - name: "FeatherOpenAPIKit", - dependencies: [ - .product(name: "OpenAPIKit30", package: "OpenAPIKit"), - - ], - swiftSettings: defaultSwiftSettings - ), .target( name: "FeatherOpenAPI", dependencies: [ @@ -82,30 +45,6 @@ let package = Package( ], swiftSettings: defaultSwiftSettings ), - // MARK: - - .testTarget( - name: "FeatherOpenAPIKitTests", - dependencies: [ - .product(name: "Yams", package: "Yams"), - .product(name: "OpenAPIKit", package: "OpenAPIKit"), - .product(name: "OpenAPIKitCompat", package: "OpenAPIKit"), - .target(name: "FeatherOpenAPIKit"), - ], - swiftSettings: defaultSwiftSettings, - ), - .testTarget( - name: "FeatherOpenAPIKitPluginTests", - dependencies: [ - .product(name: "Yams", package: "Yams"), - .product(name: "OpenAPIKit", package: "OpenAPIKit"), - .product(name: "OpenAPIKitCompat", package: "OpenAPIKit"), - .target(name: "FeatherOpenAPIKit"), - ], - swiftSettings: defaultSwiftSettings, - plugins: [ - .plugin(name: "FeatherOpenAPIKitGenerator"), - ] - ), .testTarget( name: "FeatherOpenAPITests", dependencies: [ diff --git a/Sources/FeatherOpenAPI/Callback.swift b/Sources/FeatherOpenAPI/Callback.swift deleted file mode 100644 index 4ce5a8e..0000000 --- a/Sources/FeatherOpenAPI/Callback.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public struct CallbackID: Hashable { - - public var rawValue: String - - public init( - _ rawValue: String - ) { - self.rawValue = rawValue - } -} - -public protocol CallbackRepresentable { - func openAPICallback() -> Callback -} - -//extension OpenAPI.Components: ComponentsRepresentable { -// -// public func openAPIComponents() -> OpenAPI.Components { -// self -// } -//} - -// MARK: - - -public struct Callback: CallbackRepresentable { - - public func openAPICallback() -> Callback { - fatalError() - } -} diff --git a/Sources/FeatherOpenAPI/Callback/CallbackID.swift b/Sources/FeatherOpenAPI/Callback/CallbackID.swift new file mode 100644 index 0000000..f714cad --- /dev/null +++ b/Sources/FeatherOpenAPI/Callback/CallbackID.swift @@ -0,0 +1,17 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +public struct CallbackID: Hashable { + + public var rawValue: String + + public init( + _ rawValue: String + ) { + self.rawValue = rawValue + } +} diff --git a/Sources/FeatherOpenAPI/Components/Components.swift b/Sources/FeatherOpenAPI/Components/Components.swift new file mode 100644 index 0000000..82345d2 --- /dev/null +++ b/Sources/FeatherOpenAPI/Components/Components.swift @@ -0,0 +1,40 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public struct Components: ComponentsRepresentable { + + public var schemas: OrderedDictionary + public var parameters: OrderedDictionary + public var examples: OrderedDictionary + public var responses: OrderedDictionary + public var requestBodies: OrderedDictionary + public var headers: OrderedDictionary + public var securitySchemes: OrderedDictionary + public var links: OrderedDictionary + + public init( + schemas: OrderedDictionary = [:], + parameters: OrderedDictionary = [:], + examples: OrderedDictionary = [:], + responses: OrderedDictionary = [:], + requestBodies: OrderedDictionary = [:], + headers: OrderedDictionary = [:], + securitySchemes: OrderedDictionary = [:], + links: OrderedDictionary = [:], + ) { + self.schemas = schemas + self.parameters = parameters + self.examples = examples + self.responses = responses + self.requestBodies = requestBodies + self.headers = headers + self.securitySchemes = securitySchemes + self.links = links + } +} diff --git a/Sources/FeatherOpenAPI/Components.swift b/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift similarity index 58% rename from Sources/FeatherOpenAPI/Components.swift rename to Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift index 6d17415..34511d0 100644 --- a/Sources/FeatherOpenAPI/Components.swift +++ b/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift @@ -7,63 +7,42 @@ import OpenAPIKit30 +public protocol ComponentsRepresentable: + OpenAPIComponentsRepresentable, + VendorExtensionsProperty +{ + + var schemas: OrderedDictionary { get } + var parameters: OrderedDictionary { get } + var examples: OrderedDictionary { get } + var responses: OrderedDictionary { get } + var requestBodies: OrderedDictionary { get } + var headers: OrderedDictionary { get } + var securitySchemes: OrderedDictionary { get } + var links: OrderedDictionary { get } + // public var callbacks: OrderedDictionary -public protocol ComponentsRepresentable { - func openAPIComponents() -> OpenAPI.Components -} - -extension OpenAPI.Components: ComponentsRepresentable { - - public func openAPIComponents() -> OpenAPI.Components { - self - } + func openAPISchemas() -> OpenAPI.ComponentDictionary + func openAPIParameters() -> OpenAPI.ComponentDictionary + func openAPIExamples() -> OpenAPI.ComponentDictionary + func openAPIResponses() -> OpenAPI.ComponentDictionary + func openAPIRequestBodies() -> OpenAPI.ComponentDictionary + func openAPIHeaders() -> OpenAPI.ComponentDictionary + func openAPISecuritySchemes() -> OpenAPI.ComponentDictionary + func openAPILinks() -> OpenAPI.ComponentDictionary } -// MARK: - - - -public struct Components: ComponentsRepresentable { - - public var schemas: OrderedDictionary - public var parameters: OrderedDictionary - public var examples: OrderedDictionary - public var responses: OrderedDictionary - public var requestBodies: - OrderedDictionary - public var headers: OrderedDictionary - public var securitySchemes: - OrderedDictionary - public var links: OrderedDictionary - // public var callbacks: OrderedDictionary - public var vendorExtensions: [String: AnyCodable] - - public init( - schemas: OrderedDictionary = [:], - parameters: OrderedDictionary = [:], - examples: OrderedDictionary = [:], - responses: OrderedDictionary = [:], - requestBodies: OrderedDictionary< - RequestBodyID, OpenAPIRequestBodyRepresentable - > = [:], - headers: OrderedDictionary = [:], - securitySchemes: OrderedDictionary< - SecuritySchemeID, SecuritySchemeRepresentable - > = [:], - links: OrderedDictionary = [:], - // callbacks: OrderedDictionary = [:], - vendorExtensions: [String: AnyCodable] = [:] - ) { - self.schemas = schemas - self.parameters = parameters - self.examples = examples - self.responses = responses - self.requestBodies = requestBodies - self.headers = headers - self.securitySchemes = securitySchemes - self.links = links - // self.callbacks = callbacks - self.vendorExtensions = vendorExtensions - } +public extension ComponentsRepresentable { + + var schemas: OrderedDictionary { [:] } + var parameters: OrderedDictionary { [:] } + var examples: OrderedDictionary { [:] } + var responses: OrderedDictionary { [:] } + var requestBodies: OrderedDictionary { [:] } + var headers: OrderedDictionary { [:] } + var securitySchemes: OrderedDictionary { [:] } + var links: OrderedDictionary { [:] } + func openAPISchemas() -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] @@ -73,21 +52,12 @@ public struct Components: ComponentsRepresentable { } return result } - - func openAPIResponses() -> OpenAPI.ComponentDictionary { - var result: OpenAPI.ComponentDictionary = [:] - - for (key, value) in responses { - result[.init(stringLiteral: key.rawValue)] = value.openAPIResponse() - } - return result - } - + func openAPIParameters() -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in parameters { - result[.init(stringLiteral: key.rawValue)] = value + result[.init(stringLiteral: key.rawValue)] = value.openAPIParameter() } return result } @@ -100,6 +70,15 @@ public struct Components: ComponentsRepresentable { } return result } + + func openAPIResponses() -> OpenAPI.ComponentDictionary { + var result: OpenAPI.ComponentDictionary = [:] + + for (key, value) in responses { + result[.init(stringLiteral: key.rawValue)] = value.openAPIResponse() + } + return result + } func openAPIRequestBodies() -> OpenAPI.ComponentDictionary { @@ -151,7 +130,7 @@ public struct Components: ComponentsRepresentable { // return result // } - public func openAPIComponents() -> OpenAPI.Components { + func openAPIComponents() -> OpenAPI.Components { .init( schemas: openAPISchemas(), responses: openAPIResponses(), @@ -165,5 +144,4 @@ public struct Components: ComponentsRepresentable { vendorExtensions: vendorExtensions ) } - } diff --git a/Sources/FeatherOpenAPI/Components/OpenAPIComponentsRepresentable.swift b/Sources/FeatherOpenAPI/Components/OpenAPIComponentsRepresentable.swift new file mode 100644 index 0000000..b7e3b42 --- /dev/null +++ b/Sources/FeatherOpenAPI/Components/OpenAPIComponentsRepresentable.swift @@ -0,0 +1,20 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + + +public protocol OpenAPIComponentsRepresentable { + func openAPIComponents() -> OpenAPI.Components +} + +extension OpenAPI.Components: OpenAPIComponentsRepresentable { + + public func openAPIComponents() -> OpenAPI.Components { + self + } +} diff --git a/Sources/FeatherOpenAPI/Contact.swift b/Sources/FeatherOpenAPI/Contact.swift deleted file mode 100644 index 6f448d1..0000000 --- a/Sources/FeatherOpenAPI/Contact.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public protocol ContactRepresentable { - - func openAPIContact() -> OpenAPI.Document.Info.Contact -} - -extension OpenAPI.Document.Info.Contact: ContactRepresentable { - - public func openAPIContact() -> OpenAPI.Document.Info.Contact { - self - } -} - -// MARK: - - -public struct Contact: ContactRepresentable { - - public let name: String? - public let url: URLRepresentable? - public let email: String? - public var vendorExtensions: [String: AnyCodable] - - public func openAPIContact() -> OpenAPI.Document.Info.Contact { - .init( - name: name, - url: url?.url(), - email: email, - vendorExtensions: vendorExtensions - ) - } -} diff --git a/Sources/FeatherOpenAPI/Contact/ContactRepresentable.swift b/Sources/FeatherOpenAPI/Contact/ContactRepresentable.swift new file mode 100644 index 0000000..c4c1283 --- /dev/null +++ b/Sources/FeatherOpenAPI/Contact/ContactRepresentable.swift @@ -0,0 +1,33 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol ContactRepresentable: + OpenAPIContactRepresentable, + VendorExtensionsProperty +{ + var name: String? { get } + var url: LocationRepresentable? { get } + var email: String? { get } +} + +public extension ContactRepresentable { + + var name: String? { nil } + var url: LocationRepresentable? { nil } + var email: String? { nil } + + func openAPIContact() -> OpenAPI.Document.Info.Contact { + .init( + name: name, + url: url?.openAPILocation(), + email: email, + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI/Contact/OpenAPIContactRepresentable.swift b/Sources/FeatherOpenAPI/Contact/OpenAPIContactRepresentable.swift new file mode 100644 index 0000000..609c1a2 --- /dev/null +++ b/Sources/FeatherOpenAPI/Contact/OpenAPIContactRepresentable.swift @@ -0,0 +1,20 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol OpenAPIContactRepresentable { + + func openAPIContact() -> OpenAPI.Document.Info.Contact +} + +extension OpenAPI.Document.Info.Contact: OpenAPIContactRepresentable { + + public func openAPIContact() -> OpenAPI.Document.Info.Contact { + self + } +} diff --git a/Sources/FeatherOpenAPI/Content/ContentRepresentable.swift b/Sources/FeatherOpenAPI/Content/ContentRepresentable.swift index 38947ef..a06f1c8 100644 --- a/Sources/FeatherOpenAPI/Content/ContentRepresentable.swift +++ b/Sources/FeatherOpenAPI/Content/ContentRepresentable.swift @@ -9,7 +9,8 @@ import OpenAPIKit30 public protocol ContentRepresentable: OpenAPIContentRepresentable, - ReferencedSchemaMapRepresentable + ReferencedSchemaMapRepresentable, + VendorExtensionsProperty { var schema: SchemaRepresentable { get } } @@ -21,7 +22,7 @@ public extension ContentRepresentable { schema: schema.openAPISchema(), examples: nil, encoding: nil, - vendorExtensions: [:] + vendorExtensions: vendorExtensions ) } diff --git a/Sources/FeatherOpenAPI/Document.swift b/Sources/FeatherOpenAPI/Document.swift deleted file mode 100644 index 7826df4..0000000 --- a/Sources/FeatherOpenAPI/Document.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// File 2.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public protocol DocumentRepresentable { - func openAPIDocument() -> OpenAPI.Document -} - -extension OpenAPI.Document: DocumentRepresentable { - - public func openAPIDocument() -> OpenAPI.Document { - self - } -} - -// MARK: - - -public struct Document: DocumentRepresentable { - - public var info: InfoRepresentable - public var servers: [ServerRepresentable] - public var paths: OrderedDictionary - public var components: ComponentsRepresentable - public var security: [SecurityRequirementRepresentable] - public var externalDocumentation: ExternalDocumentationRepresentable? - public var vendorExtensions: [String: AnyCodable] - - public init( - info: InfoRepresentable, - servers: [ServerRepresentable] = [], - paths: OrderedDictionary, - components: ComponentsRepresentable, - security: [SecurityRequirementRepresentable] = [], - externalDocumentation: ExternalDocumentationRepresentable? = nil, - vendorExtensions: [String: AnyCodable] = [:] - ) { - self.info = info - self.servers = servers - self.paths = paths - self.components = components - self.security = security - self.externalDocumentation = externalDocumentation - self.vendorExtensions = vendorExtensions - } - - public func openAPIDocument() -> OpenAPI.Document { - .init( - openAPIVersion: .v3_0_0, - info: info.openAPIInfo(), - servers: servers.map { $0.openAPIServer() }, - paths: self.paths.mapValues { .init($0.openAPIPathItem()) }, - components: components.openAPIComponents(), - security: security.map { $0.openAPISecurityRequirement() }, - tags: nil, - externalDocs: externalDocumentation?.openAPIExternalDocumentation(), - vendorExtensions: vendorExtensions - ) - } -} diff --git a/Sources/FeatherOpenAPI/Document/DocumentRepresentable.swift b/Sources/FeatherOpenAPI/Document/DocumentRepresentable.swift new file mode 100644 index 0000000..ee09631 --- /dev/null +++ b/Sources/FeatherOpenAPI/Document/DocumentRepresentable.swift @@ -0,0 +1,43 @@ +// +// File 2.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol DocumentRepresentable: + OpenAPIDocumentRepresentable, + VendorExtensionsProperty +{ + var info: OpenAPIInfoRepresentable { get } + var servers: [OpenAPIServerRepresentable] { get } + var paths: PathMap { get } + var components: OpenAPIComponentsRepresentable { get } + var security: [OpenAPISecurityRequirementRepresentable] { get } + var externalDocs: ExternalDocsRepresentable? { get } +} + +public extension DocumentRepresentable { + + var servers: [OpenAPIServerRepresentable] { [] } + var paths: PathMap { [:] } + + var security: [OpenAPISecurityRequirementRepresentable] { [] } + var externalDocs: ExternalDocsRepresentable? { nil } + + func openAPIDocument() -> OpenAPI.Document { + .init( + openAPIVersion: .v3_0_0, + info: info.openAPIInfo(), + servers: servers.map { $0.openAPIServer() }, + paths: paths.mapValues { .init($0.openAPIPathItem()) }, + components: components.openAPIComponents(), + security: security.map { $0.openAPISecurityRequirement() }, + tags: nil, + externalDocs: externalDocs?.openAPIExternalDocs(), + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI/Document/OpenAPIDocumentRepresentable.swift b/Sources/FeatherOpenAPI/Document/OpenAPIDocumentRepresentable.swift new file mode 100644 index 0000000..c66eb54 --- /dev/null +++ b/Sources/FeatherOpenAPI/Document/OpenAPIDocumentRepresentable.swift @@ -0,0 +1,19 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol OpenAPIDocumentRepresentable { + func openAPIDocument() -> OpenAPI.Document +} + +extension OpenAPI.Document: OpenAPIDocumentRepresentable { + + public func openAPIDocument() -> OpenAPI.Document { + self + } +} diff --git a/Sources/FeatherOpenAPI/Example.swift b/Sources/FeatherOpenAPI/Example.swift deleted file mode 100644 index 758d5cf..0000000 --- a/Sources/FeatherOpenAPI/Example.swift +++ /dev/null @@ -1,62 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public struct ExampleID: Hashable { - - public var rawValue: String - - public init( - _ rawValue: String - ) { - self.rawValue = rawValue - } -} - -public protocol ExampleRepresentable { - func openAPIExample() -> OpenAPI.Example -} - -extension OpenAPI.Example: ExampleRepresentable { - - public func openAPIExample() -> OpenAPI.Example { - self - } -} - -// MARK: - - -public struct Example: ExampleRepresentable { - - public var summary: String? - public var description: String? - public var value: AnyCodable - public var vendorExtensions: [String: AnyCodable] - - public init( - summary: String? = nil, - description: String? = nil, - value: AnyCodable, - vendorExtensions: [String: AnyCodable] = [:] - ) { - self.summary = summary - self.description = description - self.value = value - self.vendorExtensions = vendorExtensions - } - - public func openAPIExample() -> OpenAPI.Example { - .init( - summary: summary, - description: description, - value: .init(value), - vendorExtensions: vendorExtensions - ) - } - -} diff --git a/Sources/FeatherOpenAPI/Example/ExampleID.swift b/Sources/FeatherOpenAPI/Example/ExampleID.swift new file mode 100644 index 0000000..e89e013 --- /dev/null +++ b/Sources/FeatherOpenAPI/Example/ExampleID.swift @@ -0,0 +1,17 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +public struct ExampleID: Hashable { + + public var rawValue: String + + public init( + _ rawValue: String + ) { + self.rawValue = rawValue + } +} diff --git a/Sources/FeatherOpenAPI/Example/ExampleRepresentable.swift b/Sources/FeatherOpenAPI/Example/ExampleRepresentable.swift new file mode 100644 index 0000000..c933f9a --- /dev/null +++ b/Sources/FeatherOpenAPI/Example/ExampleRepresentable.swift @@ -0,0 +1,29 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol ExampleRepresentable: + OpenAPIExampleRepresentable, + DescriptionProperty, + VendorExtensionsProperty +{ + var summary: String? { get } + var value: AnyCodable { get } +} + +public extension ExampleRepresentable { + + func openAPIExample() -> OpenAPI.Example { + .init( + summary: summary, + description: description, + value: .init(value), + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI/Example/OpenAPIExampleRepresentable.swift b/Sources/FeatherOpenAPI/Example/OpenAPIExampleRepresentable.swift new file mode 100644 index 0000000..62ef17f --- /dev/null +++ b/Sources/FeatherOpenAPI/Example/OpenAPIExampleRepresentable.swift @@ -0,0 +1,19 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol OpenAPIExampleRepresentable { + func openAPIExample() -> OpenAPI.Example +} + +extension OpenAPI.Example: OpenAPIExampleRepresentable { + + public func openAPIExample() -> OpenAPI.Example { + self + } +} diff --git a/Sources/FeatherOpenAPI/ExternalDocs/ExternalDocsRepresentable.swift b/Sources/FeatherOpenAPI/ExternalDocs/ExternalDocsRepresentable.swift new file mode 100644 index 0000000..f758510 --- /dev/null +++ b/Sources/FeatherOpenAPI/ExternalDocs/ExternalDocsRepresentable.swift @@ -0,0 +1,28 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + + +import OpenAPIKit30 + +public protocol ExternalDocsRepresentable: + OpenAPIExternalDocsRepresentable, + DescriptionProperty, + VendorExtensionsProperty +{ + var url: LocationRepresentable { get } +} + +public extension ExternalDocsRepresentable { + + func openAPIExternalDocs() -> OpenAPI.ExternalDocumentation { + .init( + description: description, + url: url.openAPILocation(), + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI/ExternalDocs/OpenAPIExternalDocsRepresentable.swift b/Sources/FeatherOpenAPI/ExternalDocs/OpenAPIExternalDocsRepresentable.swift new file mode 100644 index 0000000..95aeb3b --- /dev/null +++ b/Sources/FeatherOpenAPI/ExternalDocs/OpenAPIExternalDocsRepresentable.swift @@ -0,0 +1,20 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol OpenAPIExternalDocsRepresentable { + func openAPIExternalDocs() -> OpenAPI.ExternalDocumentation +} + +extension OpenAPI.ExternalDocumentation: OpenAPIExternalDocsRepresentable { + + public func openAPIExternalDocs() -> OpenAPI.ExternalDocumentation { + self + } +} + diff --git a/Sources/FeatherOpenAPI/ExternalDocumentation.swift b/Sources/FeatherOpenAPI/ExternalDocumentation.swift deleted file mode 100644 index ebede2c..0000000 --- a/Sources/FeatherOpenAPI/ExternalDocumentation.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -#if canImport(FoundationEssentials) -import FoundationEssentials -#else -import Foundation -#endif - -import OpenAPIKit30 - -public protocol ExternalDocumentationRepresentable { - func openAPIExternalDocumentation() -> OpenAPI.ExternalDocumentation -} - -extension OpenAPI.ExternalDocumentation: ExternalDocumentationRepresentable { - - public func openAPIExternalDocumentation() -> OpenAPI.ExternalDocumentation { - self - } -} - -// MARK: - - -public struct ExternalDocumentation: ExternalDocumentationRepresentable { - public var url: String - public var description: String? - public var vendorExtensions: [String: AnyCodable] - - public init( - url: String, - description: String? = nil, - vendorExtensions: [String: AnyCodable] = [:] - ) { - self.url = url - self.description = description - self.vendorExtensions = vendorExtensions - } - - public func openAPIExternalDocumentation() -> OpenAPI.ExternalDocumentation - { - .init( - description: description, - url: .init(string: url)!, - vendorExtensions: vendorExtensions - ) - } -} diff --git a/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift b/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift index f3245e9..2ae4ce6 100644 --- a/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift +++ b/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift @@ -8,28 +8,24 @@ import OpenAPIKit30 public protocol HeaderRepresentable: - OpenAPIHeaderRepresentable + OpenAPIHeaderRepresentable, + DescriptionProperty, + RequiredProperty, + DeprecatedProperty, + VendorExtensionsProperty { var schema: OpenAPISchemaRepresentable { get } - - var description: String? { get } - var required: Bool { get } - var deprecated: Bool { get } } public extension HeaderRepresentable { - - var description: String? { nil } - var required: Bool { false } - var deprecated: Bool { false } - + func openAPIHeader() -> OpenAPI.Header { .init( schema: schema.openAPISchema(), description: description, required: required, deprecated: deprecated, - vendorExtensions: [:] + vendorExtensions: vendorExtensions ) } } diff --git a/Sources/FeatherOpenAPI/Identifiable.swift b/Sources/FeatherOpenAPI/Identifiable.swift new file mode 100644 index 0000000..3c64593 --- /dev/null +++ b/Sources/FeatherOpenAPI/Identifiable.swift @@ -0,0 +1,28 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +public protocol Identifiable: Sendable { + var openAPIIdentifier: String { get } +} + +public extension Identifiable { + + var openAPIIdentifier: String { + String(describing: type(of: self)) + } +} + + +//static var id: String { +// var components = String(reflecting: self).split(separator: ".") +// components.remove(at: 0) // remove namespace +// components.remove(at: 2) // remove enum name +// return +// components +// .joined(separator: "") +// .replacing("GenericComponent", with: "Generic") +//} diff --git a/Sources/FeatherOpenAPI/Info.swift b/Sources/FeatherOpenAPI/Info.swift deleted file mode 100644 index ca89c86..0000000 --- a/Sources/FeatherOpenAPI/Info.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public protocol InfoRepresentable { - - func openAPIInfo() -> OpenAPI.Document.Info -} - -extension OpenAPI.Document.Info: InfoRepresentable { - - public func openAPIInfo() -> OpenAPI.Document.Info { - self - } -} - -// MARK: - - - -public struct Info: InfoRepresentable { - public var title: String - public var description: String? - public var termsOfService: URLRepresentable? - public var contact: ContactRepresentable? - public var license: LicenseRepresentable? - public var version: String - public var vendorExtensions: [String: AnyCodable] - - public init( - title: String, - description: String? = nil, - termsOfService: URLRepresentable? = nil, - contact: ContactRepresentable? = nil, - license: LicenseRepresentable? = nil, - version: String, - vendorExtensions: [String: AnyCodable] = [:] - ) { - self.title = title - self.description = description - self.termsOfService = termsOfService - self.contact = contact - self.license = license - self.version = version - self.vendorExtensions = vendorExtensions - } - - public func openAPIInfo() -> OpenAPI.Document.Info { - .init( - title: title, - description: description, - termsOfService: termsOfService?.url(), - contact: contact.map { $0.openAPIContact() }, - license: license.map { $0.openAPILicense() }, - version: version, - vendorExtensions: vendorExtensions - ) - } -} diff --git a/Sources/FeatherOpenAPI/Info/InfoRepresentable.swift b/Sources/FeatherOpenAPI/Info/InfoRepresentable.swift new file mode 100644 index 0000000..5a6b935 --- /dev/null +++ b/Sources/FeatherOpenAPI/Info/InfoRepresentable.swift @@ -0,0 +1,39 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol InfoRepresentable: + OpenAPIInfoRepresentable, + DescriptionProperty, + VendorExtensionsProperty +{ + var title: String { get } + var termsOfService: LocationRepresentable? { get } + var contact: OpenAPIContactRepresentable? { get } + var license: OpenAPILicenseRepresentable? { get } + var version: String { get } +} + +public extension InfoRepresentable { + + var termsOfService: LocationRepresentable? { nil } + var contact: OpenAPIContactRepresentable? { nil } + var license: OpenAPILicenseRepresentable? { nil } + + func openAPIInfo() -> OpenAPI.Document.Info { + .init( + title: title, + description: description, + termsOfService: termsOfService?.openAPILocation(), + contact: contact.map { $0.openAPIContact() }, + license: license.map { $0.openAPILicense() }, + version: version, + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI/Info/OpenAPIInfoRepresentable.swift b/Sources/FeatherOpenAPI/Info/OpenAPIInfoRepresentable.swift new file mode 100644 index 0000000..cb4604b --- /dev/null +++ b/Sources/FeatherOpenAPI/Info/OpenAPIInfoRepresentable.swift @@ -0,0 +1,20 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol OpenAPIInfoRepresentable { + + func openAPIInfo() -> OpenAPI.Document.Info +} + +extension OpenAPI.Document.Info: OpenAPIInfoRepresentable { + + public func openAPIInfo() -> OpenAPI.Document.Info { + self + } +} diff --git a/Sources/FeatherOpenAPI/License.swift b/Sources/FeatherOpenAPI/License.swift deleted file mode 100644 index ad5acda..0000000 --- a/Sources/FeatherOpenAPI/License.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public protocol LicenseRepresentable { - func openAPILicense() -> OpenAPI.Document.Info.License -} - -extension OpenAPI.Document.Info.License: LicenseRepresentable { - - public func openAPILicense() -> OpenAPI.Document.Info.License { - self - } -} - -// MARK: - - - - -public struct License: LicenseRepresentable { - public let name: String - public let url: URLRepresentable? - public var vendorExtensions: [String: AnyCodable] - - public func openAPILicense() -> OpenAPI.Document.Info.License { - .init( - name: name, - url: url?.url(), - vendorExtensions: vendorExtensions - ) - } -} diff --git a/Sources/FeatherOpenAPI/License/LicenseRepresentable.swift b/Sources/FeatherOpenAPI/License/LicenseRepresentable.swift new file mode 100644 index 0000000..e1f7026 --- /dev/null +++ b/Sources/FeatherOpenAPI/License/LicenseRepresentable.swift @@ -0,0 +1,27 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol LicenseRepresentable: + OpenAPILicenseRepresentable, + VendorExtensionsProperty +{ + var name: String { get } + var url: LocationRepresentable? { get } +} + +extension LicenseRepresentable { + + public func openAPILicense() -> OpenAPI.Document.Info.License { + .init( + name: name, + url: url?.openAPILocation(), + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI/License/OpenAPILicenseRepresentable.swift b/Sources/FeatherOpenAPI/License/OpenAPILicenseRepresentable.swift new file mode 100644 index 0000000..25bcd00 --- /dev/null +++ b/Sources/FeatherOpenAPI/License/OpenAPILicenseRepresentable.swift @@ -0,0 +1,19 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol OpenAPILicenseRepresentable { + func openAPILicense() -> OpenAPI.Document.Info.License +} + +extension OpenAPI.Document.Info.License: OpenAPILicenseRepresentable { + + public func openAPILicense() -> OpenAPI.Document.Info.License { + self + } +} diff --git a/Sources/FeatherOpenAPI/Link/LinkID.swift b/Sources/FeatherOpenAPI/Link/LinkID.swift new file mode 100644 index 0000000..a17abea --- /dev/null +++ b/Sources/FeatherOpenAPI/Link/LinkID.swift @@ -0,0 +1,17 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +public struct LinkID: Hashable { + + public var rawValue: String + + public init( + _ rawValue: String + ) { + self.rawValue = rawValue + } +} diff --git a/Sources/FeatherOpenAPI/Link/OpenAPILinkRepresentable.swift b/Sources/FeatherOpenAPI/Link/OpenAPILinkRepresentable.swift new file mode 100644 index 0000000..178d73b --- /dev/null +++ b/Sources/FeatherOpenAPI/Link/OpenAPILinkRepresentable.swift @@ -0,0 +1,19 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol OpenAPILinkRepresentable { + func openAPILink() -> OpenAPI.Link +} + +extension OpenAPI.Link: OpenAPILinkRepresentable { + + public func openAPILink() -> OpenAPI.Link { + self + } +} diff --git a/Sources/FeatherOpenAPI/Links.swift b/Sources/FeatherOpenAPI/Links.swift deleted file mode 100644 index 76e2595..0000000 --- a/Sources/FeatherOpenAPI/Links.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public struct LinkID: Hashable { - - public var rawValue: String - - public init( - _ rawValue: String - ) { - self.rawValue = rawValue - } -} - -public protocol LinkRepresentable { - func openAPILink() -> OpenAPI.Link -} - -extension OpenAPI.Link: LinkRepresentable { - - public func openAPILink() -> OpenAPI.Link { - self - } -} - -// MARK: - - -public struct Link: LinkRepresentable { - - public func openAPILink() -> OpenAPI.Link { - fatalError() - // .init( - // operationId: <#T##String#>, - // parameters: <#T##OrderedDictionary>#>, - // requestBody: <#T##Either?#>, - // description: <#T##String?#>, - // server: <#T##OpenAPI.Server?#>, - // vendorExtensions: <#T##[String : AnyCodable]#> - // ) - } -} diff --git a/Sources/FeatherOpenAPI/Location.swift b/Sources/FeatherOpenAPI/Location.swift deleted file mode 100644 index 42865b7..0000000 --- a/Sources/FeatherOpenAPI/Location.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -#if canImport(FoundationEssentials) -import FoundationEssentials -#else -import Foundation -#endif - -public protocol URLRepresentable { - func url() -> URL -} - -extension URL: URLRepresentable { - - public func url() -> URL { - self - } -} - -// MARK: - - -public struct Location: URLRepresentable { - - public var string: String - - public init( - _ string: String - ) { - self.string = string - } - - public func url() -> URL { - .init(string: string)! - } -} diff --git a/Sources/FeatherOpenAPI/Location/LocationRepresentable.swift b/Sources/FeatherOpenAPI/Location/LocationRepresentable.swift new file mode 100644 index 0000000..74206e7 --- /dev/null +++ b/Sources/FeatherOpenAPI/Location/LocationRepresentable.swift @@ -0,0 +1,28 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +public protocol LocationRepresentable: OpenAPILocationRepresentable { + var location: String { get } +} + +public extension LocationRepresentable { + + func openAPILocation() -> URL { + .init(string: location)! + } +} + +//extension String: URLRepresentable { +// public var rawURL: String { self } +//} + diff --git a/Sources/FeatherOpenAPI/Location/OpenAPILocationRepresentable.swift b/Sources/FeatherOpenAPI/Location/OpenAPILocationRepresentable.swift new file mode 100644 index 0000000..e1d9080 --- /dev/null +++ b/Sources/FeatherOpenAPI/Location/OpenAPILocationRepresentable.swift @@ -0,0 +1,23 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +public protocol OpenAPILocationRepresentable { + func openAPILocation() -> URL +} + +extension URL: OpenAPILocationRepresentable { + + public func openAPILocation() -> URL { + self + } +} diff --git a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift index 002c2ca..aa9e0b6 100644 --- a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift +++ b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift @@ -10,20 +10,21 @@ import OpenAPIKit30 public protocol OperationRepresentable: OpenAPIOperationRepresentable, - ReferencedSchemaMapRepresentable + ReferencedSchemaMapRepresentable, + DescriptionProperty, + DeprecatedProperty, + VendorExtensionsProperty { // associatedtype RequestBodyType: RequestBodyRepresentable // var tags: [String]? var summary: String? { get } - var description: String? { get } var operationId: String? { get } var parameters: [ParameterRepresentable] { get } var requestBody: RequestBodyRepresentable? { get } var responseMap: ResponseMap { get } - var deprecated: Bool { get } // var security: [SecurityRequirementRepresentable]? // var servers: [ServerRepresentable]? } @@ -31,14 +32,12 @@ public protocol OperationRepresentable: public extension OperationRepresentable { var summary: String? { nil } - var description: String? { nil } + var operationId: String? { nil } var parameters: [ParameterRepresentable] { [] } var requestBody: RequestBodyRepresentable? { nil } - var deprecated: Bool { false } - func openAPIOperation() -> OpenAPI.Operation { .init( tags: nil, @@ -53,7 +52,7 @@ public extension OperationRepresentable { deprecated: deprecated, security: nil, servers: nil, - vendorExtensions: [:] + vendorExtensions: vendorExtensions ) } diff --git a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift index d30433f..5fff43f 100644 --- a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift +++ b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift @@ -8,19 +8,18 @@ import OpenAPIKit30 public protocol ParameterRepresentable: - OpenAPIParameterRepresentable + OpenAPIParameterRepresentable, + DescriptionProperty, + DeprecatedProperty, + VendorExtensionsProperty { var name: String { get } var context: OpenAPI.Parameter.Context { get } var schema: OpenAPISchemaRepresentable { get } - var description: String? { get } - var deprecated: Bool { get } + } public extension ParameterRepresentable { - - var description: String? { nil } - var deprecated: Bool { false } func openAPIParameter() -> OpenAPI.Parameter { .init( @@ -29,7 +28,7 @@ public extension ParameterRepresentable { schema: schema.openAPISchema(), description: description, deprecated: deprecated, - vendorExtensions: [:] + vendorExtensions: vendorExtensions ) } } diff --git a/Sources/FeatherOpenAPI/Parameter/CookieParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/CookieParameterRepresentable.swift index 71b6b48..5a77b85 100644 --- a/Sources/FeatherOpenAPI/Parameter/CookieParameterRepresentable.swift +++ b/Sources/FeatherOpenAPI/Parameter/CookieParameterRepresentable.swift @@ -7,16 +7,16 @@ import OpenAPIKit30 -public protocol CookieParameterRepresentable: ParameterRepresentable { - - var required: Bool { get } +public protocol CookieParameterRepresentable: + ParameterRepresentable, + RequiredProperty +{ + } public extension CookieParameterRepresentable { - - var required: Bool { false } var context: OpenAPI.Parameter.Context { - .cookie(required: required) + .cookie(required: `required`) } } diff --git a/Sources/FeatherOpenAPI/Parameter/HeaderParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/HeaderParameterRepresentable.swift index a55f08d..2c93713 100644 --- a/Sources/FeatherOpenAPI/Parameter/HeaderParameterRepresentable.swift +++ b/Sources/FeatherOpenAPI/Parameter/HeaderParameterRepresentable.swift @@ -7,16 +7,16 @@ import OpenAPIKit30 -public protocol HeaderParameterRepresentable: ParameterRepresentable { +public protocol HeaderParameterRepresentable: + ParameterRepresentable, + RequiredProperty +{ - var required: Bool { get } } public extension HeaderParameterRepresentable { - - var required: Bool { false } var context: OpenAPI.Parameter.Context { - .header(required: required) + .header(required: `required`) } } diff --git a/Sources/FeatherOpenAPI/Parameter/QueryParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/QueryParameterRepresentable.swift index 2e5d5a6..69b906e 100644 --- a/Sources/FeatherOpenAPI/Parameter/QueryParameterRepresentable.swift +++ b/Sources/FeatherOpenAPI/Parameter/QueryParameterRepresentable.swift @@ -7,20 +7,20 @@ import OpenAPIKit30 -public protocol QueryParameterRepresentable: ParameterRepresentable { - - var required: Bool { get } +public protocol QueryParameterRepresentable: + ParameterRepresentable, + RequiredProperty +{ var allowEmptyValue: Bool { get } } public extension QueryParameterRepresentable { - var required: Bool { false } var allowEmptyValue: Bool { true } var context: OpenAPI.Parameter.Context { .query( - required: required, + required: `required`, allowEmptyValue: allowEmptyValue ) } diff --git a/Sources/FeatherOpenAPI/Path/PathCollectionRepresentable.swift b/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift similarity index 72% rename from Sources/FeatherOpenAPI/Path/PathCollectionRepresentable.swift rename to Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift index 49542eb..a1416bc 100644 --- a/Sources/FeatherOpenAPI/Path/PathCollectionRepresentable.swift +++ b/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift @@ -30,19 +30,8 @@ extension PathCollectionRepresentable { } var components: Components { - - - - return .init( + .init( schemas: referencedSchemaMap.mapValues { $0.openAPISchema() }, - parameters: .init(), - examples: .init(), - responses: .init(), - requestBodies: .init(), - headers: .init(), - securitySchemes: .init(), - links: .init(), - vendorExtensions: .init() ) } } diff --git a/Sources/FeatherOpenAPI/Path/OpenAPIPathItemRepresentable.swift b/Sources/FeatherOpenAPI/PathItem/OpenAPIPathItemRepresentable.swift similarity index 100% rename from Sources/FeatherOpenAPI/Path/OpenAPIPathItemRepresentable.swift rename to Sources/FeatherOpenAPI/PathItem/OpenAPIPathItemRepresentable.swift diff --git a/Sources/FeatherOpenAPI/Path/PathItemRepresentable.swift b/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift similarity index 89% rename from Sources/FeatherOpenAPI/Path/PathItemRepresentable.swift rename to Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift index 0bf57ba..9851368 100644 --- a/Sources/FeatherOpenAPI/Path/PathItemRepresentable.swift +++ b/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift @@ -9,11 +9,13 @@ import OpenAPIKit30 public protocol PathItemRepresentable: OpenAPIPathItemRepresentable, - ReferencedSchemaMapRepresentable + ReferencedSchemaMapRepresentable, + DescriptionProperty, + VendorExtensionsProperty { var summary: String? { get } - var description: String? { get } - var servers: [ServerRepresentable]? { get } + + var servers: [OpenAPIServerRepresentable]? { get } var get: OperationRepresentable? { get } var put: OperationRepresentable? { get } var post: OperationRepresentable? { get } @@ -27,8 +29,8 @@ public protocol PathItemRepresentable: public extension PathItemRepresentable { var summary: String? { nil } - var description: String? { nil } - var servers: [ServerRepresentable]? { nil } + + var servers: [OpenAPIServerRepresentable]? { nil } var get: OperationRepresentable? { nil } var put: OperationRepresentable? { nil } var post: OperationRepresentable? { nil } @@ -52,7 +54,7 @@ public extension PathItemRepresentable { head: head?.openAPIOperation(), patch: patch?.openAPIOperation(), trace: trace?.openAPIOperation(), - vendorExtensions: [:] + vendorExtensions: vendorExtensions ) } diff --git a/Sources/FeatherOpenAPI/Path/PathMap.swift b/Sources/FeatherOpenAPI/PathItem/PathMap.swift similarity index 100% rename from Sources/FeatherOpenAPI/Path/PathMap.swift rename to Sources/FeatherOpenAPI/PathItem/PathMap.swift diff --git a/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyProperties.swift b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyProperties.swift deleted file mode 100644 index 1c64c04..0000000 --- a/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyProperties.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 22.. -// - - -// MARK: - description - -public protocol RequestBodyPropertyDescription { - var description: String? { get } -} - -public extension RequestBodyPropertyDescription { - var description: String? { nil } -} - -// MARK: - required - -public protocol RequestBodyPropertyRequired { - var required: Bool { get } -} - -public extension RequestBodyPropertyRequired { - var required: Bool { true } -} diff --git a/Sources/FeatherOpenAPI/RequestBody/RequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyRepresentable.swift similarity index 75% rename from Sources/FeatherOpenAPI/RequestBody/RequestBodyRepresentable.swift rename to Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyRepresentable.swift index e7097ef..fa13982 100644 --- a/Sources/FeatherOpenAPI/RequestBody/RequestBodyRepresentable.swift +++ b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyRepresentable.swift @@ -10,8 +10,9 @@ import OpenAPIKit30 public protocol RequestBodyRepresentable: Identifiable, OpenAPIRequestBodyRepresentable, - RequestBodyPropertyDescription, - RequestBodyPropertyRequired + DescriptionProperty, + RequiredProperty, + VendorExtensionsProperty { var contentMap: ContentMap { get } } @@ -22,8 +23,8 @@ extension RequestBodyRepresentable { .init( description: description, content: contentMap.mapValues { $0.openAPIContent() }, - required: required, - vendorExtensions: [:] + required: `required`, + vendorExtensions: vendorExtensions ) } } diff --git a/Sources/FeatherOpenAPI/Response/Abstraction/ResponseRepresentable.swift b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseRepresentable.swift index 5e43009..16ec2c0 100644 --- a/Sources/FeatherOpenAPI/Response/Abstraction/ResponseRepresentable.swift +++ b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseRepresentable.swift @@ -8,7 +8,8 @@ import OpenAPIKit30 public protocol ResponseRepresentable: - OpenAPIResponseRepresentable + OpenAPIResponseRepresentable, + VendorExtensionsProperty { var description: String { get } var headerMap: HeaderMap { get } @@ -25,7 +26,7 @@ public extension ResponseRepresentable { headers: headerMap.mapValues { .init($0.openAPIHeader()) }, content: contentMap.mapValues { $0.openAPIContent() }, links: [:], - vendorExtensions: [:] + vendorExtensions: vendorExtensions ) } } diff --git a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaProperties.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaProperties.swift deleted file mode 100644 index a99f9fb..0000000 --- a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaProperties.swift +++ /dev/null @@ -1,96 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 22.. -// - -import OpenAPIKit30 - -// MARK: - required - -public protocol SchemaPropertyRequired { - var required: Bool { get } -} - -public extension SchemaPropertyRequired { - var required: Bool { true } -} - -// MARK: - description - -public protocol SchemaPropertyDescription { - var description: String? { get } -} - -public extension SchemaPropertyDescription { - var description: String? { nil } -} - -// MARK: - title - -public protocol SchemaPropertyTitle { - var title: String? { get } -} - -public extension SchemaPropertyTitle { - var title: String? { nil } -} - -// MARK: - deprecated - -public protocol SchemaPropertyDeprecated { - var deprecated: Bool? { get } -} - -public extension SchemaPropertyDeprecated { - var deprecated: Bool? { nil } -} - -// MARK: - nullable - -public protocol SchemaPropertyNullable { - var nullable: Bool? { get } -} - -public extension SchemaPropertyNullable { - var nullable: Bool? { nil } -} - - -// MARK: - example - -public protocol SchemaPropertyExample { - associatedtype ExamplePropertyType = AnyCodable - - var example: ExamplePropertyType? { get } -} - -public extension SchemaPropertyExample { - var example: ExamplePropertyType? { nil } -} - -// MARK: - defaultValue - -public protocol SchemaPropertyDefaultValue { - associatedtype DefaultValuePropertyType = AnyCodable - - var defaultValue: DefaultValuePropertyType? { get } -} - -public extension SchemaPropertyDefaultValue { - var defaultValue: DefaultValuePropertyType? { nil } -} - - -// MARK: - allowedValues - -public protocol SchemaPropertyAllowedValues { - associatedtype AllowedValuesPropertyType = AnyCodable - - var allowedValues: [AllowedValuesPropertyType]? { get } -} - -public extension SchemaPropertyAllowedValues { - var allowedValues: [AllowedValuesPropertyType]? { nil } -} diff --git a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaReference.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaReference.swift index 3b333c0..893637d 100644 --- a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaReference.swift +++ b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaReference.swift @@ -12,8 +12,10 @@ public protocol SchemaReferenceRepresentable { var object: SchemaRepresentable { get } } -public struct SchemaReference: OpenAPISchemaRepresentable, SchemaReferenceRepresentable { - +public struct SchemaReference: + OpenAPISchemaRepresentable, + SchemaReferenceRepresentable +{ public var object: any SchemaRepresentable { _object } diff --git a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift index 41fd41d..264c40c 100644 --- a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift @@ -10,14 +10,21 @@ import OpenAPIKit30 public protocol SchemaRepresentable: OpenAPISchemaRepresentable, Identifiable, - ReferencedSchemaMapRepresentable + ReferencedSchemaMapRepresentable, + // shared properties + RequiredProperty, + TitleProperty, + DescriptionProperty, + NullableProperty { - + var deprecated: Bool? { get } } -extension SchemaRepresentable { +public extension SchemaRepresentable { + + var deprecated: Bool? { nil } - public var referencedSchemaMap: OrderedDictionary { + var referencedSchemaMap: OrderedDictionary { [:] } } diff --git a/Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift index 79e39e2..bf86bde 100644 --- a/Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift @@ -8,12 +8,7 @@ import OpenAPIKit30 public protocol ArraySchemaRepresentable: - SchemaRepresentable, - SchemaPropertyRequired, - SchemaPropertyTitle, - SchemaPropertyDescription, - SchemaPropertyDeprecated, - SchemaPropertyNullable + SchemaRepresentable { var items: JSONSchema? { get } } @@ -23,7 +18,7 @@ public extension ArraySchemaRepresentable { func openAPISchema() -> JSONSchema { .array( format: .generic, - required: required, + required: `required`, nullable: nullable, permissions: nil, deprecated: deprecated, diff --git a/Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift index fbf21eb..b861d6d 100644 --- a/Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift @@ -9,13 +9,8 @@ import OpenAPIKit30 public protocol BoolSchemaRepresentable: SchemaRepresentable, - SchemaPropertyRequired, - SchemaPropertyTitle, - SchemaPropertyDescription, - SchemaPropertyDeprecated, - SchemaPropertyNullable, - SchemaPropertyExample, - SchemaPropertyDefaultValue + ExampleProperty, + DefaultValueProperty where ExamplePropertyType == Bool, DefaultValuePropertyType == Bool @@ -28,7 +23,7 @@ public extension BoolSchemaRepresentable { func openAPISchema() -> JSONSchema { .boolean( format: .generic, - required: required, + required: `required`, nullable: nullable, permissions: nil, deprecated: deprecated, diff --git a/Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift index 88c1417..87823c4 100644 --- a/Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift @@ -9,14 +9,9 @@ import OpenAPIKit30 public protocol DoubleSchemaRepresentable: SchemaRepresentable, - SchemaPropertyRequired, - SchemaPropertyTitle, - SchemaPropertyDescription, - SchemaPropertyDeprecated, - SchemaPropertyNullable, - SchemaPropertyExample, - SchemaPropertyDefaultValue, - SchemaPropertyAllowedValues + ExampleProperty, + DefaultValueProperty, + AllowedValuesProperty where ExamplePropertyType == Double, DefaultValuePropertyType == Double, @@ -30,7 +25,7 @@ public extension DoubleSchemaRepresentable { func openAPISchema() -> JSONSchema { .number( format: .double, - required: required, + required: `required`, nullable: nullable, permissions: nil, deprecated: deprecated, diff --git a/Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift index 2f143af..99463cf 100644 --- a/Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift @@ -9,14 +9,9 @@ import OpenAPIKit30 public protocol FloatSchemaRepresentable: SchemaRepresentable, - SchemaPropertyRequired, - SchemaPropertyTitle, - SchemaPropertyDescription, - SchemaPropertyDeprecated, - SchemaPropertyNullable, - SchemaPropertyExample, - SchemaPropertyDefaultValue, - SchemaPropertyAllowedValues + ExampleProperty, + DefaultValueProperty, + AllowedValuesProperty where ExamplePropertyType == Float, DefaultValuePropertyType == Float, @@ -30,7 +25,7 @@ public extension FloatSchemaRepresentable { func openAPISchema() -> JSONSchema { .number( format: .float, - required: required, + required: `required`, nullable: nullable, permissions: nil, deprecated: deprecated, diff --git a/Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift index 00be643..67b710c 100644 --- a/Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift @@ -9,14 +9,9 @@ import OpenAPIKit30 public protocol Int32SchemaRepresentable: SchemaRepresentable, - SchemaPropertyRequired, - SchemaPropertyTitle, - SchemaPropertyDescription, - SchemaPropertyDeprecated, - SchemaPropertyNullable, - SchemaPropertyExample, - SchemaPropertyDefaultValue, - SchemaPropertyAllowedValues + ExampleProperty, + DefaultValueProperty, + AllowedValuesProperty where ExamplePropertyType == Int32, DefaultValuePropertyType == Int32, @@ -30,7 +25,7 @@ public extension Int32SchemaRepresentable { func openAPISchema() -> JSONSchema { .integer( format: .int32, - required: required, + required: `required`, nullable: nullable, permissions: nil, deprecated: deprecated, diff --git a/Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift index 041a91a..a3292bf 100644 --- a/Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift @@ -9,14 +9,9 @@ import OpenAPIKit30 public protocol Int64SchemaRepresentable: SchemaRepresentable, - SchemaPropertyRequired, - SchemaPropertyTitle, - SchemaPropertyDescription, - SchemaPropertyDeprecated, - SchemaPropertyNullable, - SchemaPropertyExample, - SchemaPropertyDefaultValue, - SchemaPropertyAllowedValues + ExampleProperty, + DefaultValueProperty, + AllowedValuesProperty where ExamplePropertyType == Int64, DefaultValuePropertyType == Int64, @@ -30,7 +25,7 @@ public extension Int64SchemaRepresentable { func openAPISchema() -> JSONSchema { .integer( format: .int64, - required: required, + required: `required`, nullable: nullable, permissions: nil, deprecated: deprecated, diff --git a/Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift index 165e509..164079f 100644 --- a/Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift @@ -9,14 +9,9 @@ import OpenAPIKit30 public protocol IntSchemaRepresentable: SchemaRepresentable, - SchemaPropertyRequired, - SchemaPropertyTitle, - SchemaPropertyDescription, - SchemaPropertyDeprecated, - SchemaPropertyNullable, - SchemaPropertyExample, - SchemaPropertyDefaultValue, - SchemaPropertyAllowedValues + ExampleProperty, + DefaultValueProperty, + AllowedValuesProperty where ExamplePropertyType == Int, DefaultValuePropertyType == Int, @@ -30,7 +25,7 @@ public extension IntSchemaRepresentable { func openAPISchema() -> JSONSchema { .integer( format: .unspecified, - required: required, + required: `required`, nullable: nullable, permissions: nil, deprecated: deprecated, diff --git a/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift index d93f447..411d17e 100644 --- a/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift @@ -9,12 +9,9 @@ import OpenAPIKit30 public protocol ObjectSchemaRepresentable: SchemaRepresentable, - SchemaPropertyRequired, - SchemaPropertyTitle, - SchemaPropertyDescription, - SchemaPropertyDeprecated, - SchemaPropertyNullable, - SchemaPropertyExample where ExamplePropertyType == AnyCodable + ExampleProperty +where + ExamplePropertyType == AnyCodable { var propertyMap: SchemaMap { get } } @@ -24,7 +21,7 @@ public extension ObjectSchemaRepresentable { func openAPISchema() -> JSONSchema { .object( format: .generic, - required: required, + required: `required`, nullable: nullable, permissions: nil, deprecated: deprecated, diff --git a/Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift index 0cd278c..a9a49dc 100644 --- a/Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift @@ -9,14 +9,9 @@ import OpenAPIKit30 public protocol StringSchemaRepresentable: SchemaRepresentable, - SchemaPropertyRequired, - SchemaPropertyTitle, - SchemaPropertyDescription, - SchemaPropertyNullable, - SchemaPropertyDeprecated, - SchemaPropertyExample, - SchemaPropertyDefaultValue, - SchemaPropertyAllowedValues + ExampleProperty, + DefaultValueProperty, + AllowedValuesProperty where ExamplePropertyType == String, DefaultValuePropertyType == String, @@ -30,7 +25,7 @@ public extension StringSchemaRepresentable { func openAPISchema() -> JSONSchema { .string( format: .generic, - required: required, + required: `required`, nullable: nullable, permissions: nil, deprecated: deprecated, diff --git a/Sources/FeatherOpenAPI/SecurityRequirement.swift b/Sources/FeatherOpenAPI/SecurityRequirement.swift deleted file mode 100644 index ecaa1f9..0000000 --- a/Sources/FeatherOpenAPI/SecurityRequirement.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public protocol SecurityRequirementRepresentable { - - // [JSONReference: [String]] - func openAPISecurityRequirement() -> OpenAPI.SecurityRequirement -} - -extension OpenAPI.SecurityRequirement: SecurityRequirementRepresentable { - - public func openAPISecurityRequirement() -> OpenAPI.SecurityRequirement { - self - } -} - -// MARK: - - -public struct SecurityRequirement: SecurityRequirementRepresentable { - - public var requirements: [String: [String]] - - public init( - _ requirements: [String: [String]] - ) { - self.requirements = requirements - } - - //[JSONReference: [String]] - public func openAPISecurityRequirement() -> OpenAPI.SecurityRequirement { - var result: [JSONReference: [String]] = [:] - for (key, value) in self.requirements { - result[.component(named: key)] = value - } - return result - } -} diff --git a/Sources/FeatherOpenAPI/SecurityRequirement/OpenAPISecurityRequirementRepresentable.swift b/Sources/FeatherOpenAPI/SecurityRequirement/OpenAPISecurityRequirementRepresentable.swift new file mode 100644 index 0000000..1945836 --- /dev/null +++ b/Sources/FeatherOpenAPI/SecurityRequirement/OpenAPISecurityRequirementRepresentable.swift @@ -0,0 +1,21 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol OpenAPISecurityRequirementRepresentable { + + // [JSONReference: [String]] + func openAPISecurityRequirement() -> OpenAPI.SecurityRequirement +} + +extension OpenAPI.SecurityRequirement: OpenAPISecurityRequirementRepresentable { + + public func openAPISecurityRequirement() -> OpenAPI.SecurityRequirement { + self + } +} diff --git a/Sources/FeatherOpenAPI/SecurityRequirement/SecurityRequirementRepresentable.swift b/Sources/FeatherOpenAPI/SecurityRequirement/SecurityRequirementRepresentable.swift new file mode 100644 index 0000000..a4b408d --- /dev/null +++ b/Sources/FeatherOpenAPI/SecurityRequirement/SecurityRequirementRepresentable.swift @@ -0,0 +1,25 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol SecurityRequirementRepresentable: OpenAPISecurityRequirementRepresentable { + + var requirements: [String: [String]] { get } +} + +public extension SecurityRequirementRepresentable { + + //[JSONReference: [String]] + func openAPISecurityRequirement() -> OpenAPI.SecurityRequirement { + var result: [JSONReference: [String]] = [:] + for (key, value) in self.requirements { + result[.component(named: key)] = value + } + return result + } +} diff --git a/Sources/FeatherOpenAPI/SecurityScheme.swift b/Sources/FeatherOpenAPI/SecurityScheme.swift deleted file mode 100644 index e27ecea..0000000 --- a/Sources/FeatherOpenAPI/SecurityScheme.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public struct SecuritySchemeID: Hashable { - - public var rawValue: String - - public init( - _ rawValue: String - ) { - self.rawValue = rawValue - } -} - -public protocol SecuritySchemeRepresentable { - func openAPISecurityScheme() -> OpenAPI.SecurityScheme -} - -extension OpenAPI.SecurityScheme: SecuritySchemeRepresentable { - - public func openAPISecurityScheme() -> OpenAPI.SecurityScheme { - self - } -} - -// MARK: - - -public struct SecurityScheme: SecuritySchemeRepresentable { - - public var type: OpenAPI.SecurityScheme.SecurityType - public var description: String? - public var vendorExtensions: [String: AnyCodable] - - public func openAPISecurityScheme() -> OpenAPI.SecurityScheme { - .init( - type: type, - description: description, - vendorExtensions: vendorExtensions - ) - } -} diff --git a/Sources/FeatherOpenAPI/SecurityScheme/OpenAPISecuritySchemeRepresentable.swift b/Sources/FeatherOpenAPI/SecurityScheme/OpenAPISecuritySchemeRepresentable.swift new file mode 100644 index 0000000..d6d720f --- /dev/null +++ b/Sources/FeatherOpenAPI/SecurityScheme/OpenAPISecuritySchemeRepresentable.swift @@ -0,0 +1,19 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol OpenAPISecuritySchemeRepresentable { + func openAPISecurityScheme() -> OpenAPI.SecurityScheme +} + +extension OpenAPI.SecurityScheme: OpenAPISecuritySchemeRepresentable { + + public func openAPISecurityScheme() -> OpenAPI.SecurityScheme { + self + } +} diff --git a/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeID.swift b/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeID.swift new file mode 100644 index 0000000..9a958b3 --- /dev/null +++ b/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeID.swift @@ -0,0 +1,17 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +public struct SecuritySchemeID: Hashable { + + public var rawValue: String + + public init( + _ rawValue: String + ) { + self.rawValue = rawValue + } +} diff --git a/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeRepresentable.swift b/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeRepresentable.swift new file mode 100644 index 0000000..b55fa73 --- /dev/null +++ b/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeRepresentable.swift @@ -0,0 +1,27 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol SecuritySchemeRepresentable: + OpenAPISecuritySchemeRepresentable, + DescriptionProperty, + VendorExtensionsProperty +{ + var type: OpenAPI.SecurityScheme.SecurityType { get } +} + +public extension SecuritySchemeRepresentable { + + func openAPISecurityScheme() -> OpenAPI.SecurityScheme { + .init( + type: type, + description: description, + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI/Server.swift b/Sources/FeatherOpenAPI/Server.swift deleted file mode 100644 index 0858510..0000000 --- a/Sources/FeatherOpenAPI/Server.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// File 2.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public protocol ServerRepresentable { - func openAPIServer() -> OpenAPI.Server -} - -extension OpenAPI.Server: ServerRepresentable { - - public func openAPIServer() -> OpenAPI.Server { - self - } -} - -// MARK: - - -public struct Server: ServerRepresentable { - - public var url: URLRepresentable - public var description: String? - public var variables: OrderedDictionary - public var vendorExtensions: [String: AnyCodable] - - public func openAPIServer() -> OpenAPI.Server { - .init( - url: url.url(), - description: description, - variables: variables.mapValues { - $0.openAPIServerVariable() - }, - vendorExtensions: vendorExtensions - ) - } -} diff --git a/Sources/FeatherOpenAPI/Server/OpenAPIServerRepresentable.swift b/Sources/FeatherOpenAPI/Server/OpenAPIServerRepresentable.swift new file mode 100644 index 0000000..0cc8fc0 --- /dev/null +++ b/Sources/FeatherOpenAPI/Server/OpenAPIServerRepresentable.swift @@ -0,0 +1,19 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol OpenAPIServerRepresentable { + func openAPIServer() -> OpenAPI.Server +} + +extension OpenAPI.Server: OpenAPIServerRepresentable { + + public func openAPIServer() -> OpenAPI.Server { + self + } +} diff --git a/Sources/FeatherOpenAPI/Server/ServerRepresentable.swift b/Sources/FeatherOpenAPI/Server/ServerRepresentable.swift new file mode 100644 index 0000000..015fffa --- /dev/null +++ b/Sources/FeatherOpenAPI/Server/ServerRepresentable.swift @@ -0,0 +1,36 @@ +// +// File 2.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol ServerRepresentable: + OpenAPIServerRepresentable, + DescriptionProperty, + VendorExtensionsProperty +{ + + var url: LocationRepresentable { get } + + var variables: VariableMap { get } + +} + +public extension ServerRepresentable { + + var variables: VariableMap { .init() } + + func openAPIServer() -> OpenAPI.Server { + .init( + url: url.openAPILocation(), + description: description, + variables: variables.mapValues { + $0.openAPIServerVariable() + }, + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI/Tag.swift b/Sources/FeatherOpenAPI/Tag.swift deleted file mode 100644 index 4472b7a..0000000 --- a/Sources/FeatherOpenAPI/Tag.swift +++ /dev/null @@ -1,7 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - diff --git a/Sources/FeatherOpenAPI/Tag/OpenAPITagRepresentable.swift b/Sources/FeatherOpenAPI/Tag/OpenAPITagRepresentable.swift new file mode 100644 index 0000000..6fefb57 --- /dev/null +++ b/Sources/FeatherOpenAPI/Tag/OpenAPITagRepresentable.swift @@ -0,0 +1,19 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol OpenAPITagRepresentable { + func openAPITag() -> OpenAPI.Tag +} + +extension OpenAPI.Tag: OpenAPITagRepresentable { + + public func openAPITag() -> OpenAPI.Tag { + self + } +} diff --git a/Sources/FeatherOpenAPI/Tag/TagRepresentable.swift b/Sources/FeatherOpenAPI/Tag/TagRepresentable.swift new file mode 100644 index 0000000..2f91dfa --- /dev/null +++ b/Sources/FeatherOpenAPI/Tag/TagRepresentable.swift @@ -0,0 +1,33 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol TagRepresentable: + OpenAPITagRepresentable, + DescriptionProperty, + VendorExtensionsProperty +{ + var name: String { get } + var externalDocs: ExternalDocsRepresentable? { get } +} + +extension TagRepresentable { + + var externalDocs: ExternalDocsRepresentable? { nil } + + public func openAPITag() -> OpenAPI.Tag { + .init( + name: name, + description: description, + externalDocs: externalDocs?.openAPIExternalDocs(), + vendorExtensions: vendorExtensions + ) + } +} + + diff --git a/Sources/FeatherOpenAPI/Variable.swift b/Sources/FeatherOpenAPI/Variable.swift deleted file mode 100644 index 69bda79..0000000 --- a/Sources/FeatherOpenAPI/Variable.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// File 2.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 21.. -// - -import OpenAPIKit30 - -public protocol ServerVariableRepresentable { - func openAPIServerVariable() -> OpenAPI.Server.Variable -} - -extension OpenAPI.Server.Variable: ServerVariableRepresentable { - - public func openAPIServerVariable() -> OpenAPI.Server.Variable { - self - } -} - -// MARK: - - -public struct ServerVariable: ServerVariableRepresentable { - public var `enum`: [String] - public var `default`: String - public var description: String? - public var vendorExtensions: [String: AnyCodable] - - public func openAPIServerVariable() -> OpenAPI.Server.Variable { - .init( - enum: self.enum, - default: self.default, - description: self.description, - vendorExtensions: self.vendorExtensions - ) - } -} diff --git a/Sources/FeatherOpenAPI/Variable/OpenAPIVariableRepresentable.swift b/Sources/FeatherOpenAPI/Variable/OpenAPIVariableRepresentable.swift new file mode 100644 index 0000000..501cba2 --- /dev/null +++ b/Sources/FeatherOpenAPI/Variable/OpenAPIVariableRepresentable.swift @@ -0,0 +1,19 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol OpenAPIVariableRepresentable { + func openAPIServerVariable() -> OpenAPI.Server.Variable +} + +extension OpenAPI.Server.Variable: OpenAPIVariableRepresentable { + + public func openAPIServerVariable() -> OpenAPI.Server.Variable { + self + } +} diff --git a/Sources/FeatherOpenAPI/Variable/VariableMap.swift b/Sources/FeatherOpenAPI/Variable/VariableMap.swift new file mode 100644 index 0000000..a6be365 --- /dev/null +++ b/Sources/FeatherOpenAPI/Variable/VariableMap.swift @@ -0,0 +1,13 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public typealias VariableMap = OrderedDictionary< + String, + OpenAPIVariableRepresentable +> diff --git a/Sources/FeatherOpenAPI/Variable/VariableRepresentable.swift b/Sources/FeatherOpenAPI/Variable/VariableRepresentable.swift new file mode 100644 index 0000000..834fce4 --- /dev/null +++ b/Sources/FeatherOpenAPI/Variable/VariableRepresentable.swift @@ -0,0 +1,29 @@ +// +// File 2.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 21.. +// + +import OpenAPIKit30 + +public protocol VariableRepresentable: + OpenAPIVariableRepresentable, + DescriptionProperty, + VendorExtensionsProperty +{ + var `enum`: [String] { get } + var `default`: String { get } +} + +public extension VariableRepresentable { + + func openAPIServerVariable() -> OpenAPI.Server.Variable { + .init( + enum: `enum`, + default: `default`, + description: description, + vendorExtensions: vendorExtensions + ) + } +} diff --git a/Sources/FeatherOpenAPI/_Identifiable.swift b/Sources/FeatherOpenAPI/_Identifiable.swift deleted file mode 100644 index 1adeab2..0000000 --- a/Sources/FeatherOpenAPI/_Identifiable.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 22.. -// - -public protocol Identifiable: Sendable { - var openAPIIdentifier: String { get } -} - -public extension Identifiable { - - var openAPIIdentifier: String { - String(describing: type(of: self)) - } -} diff --git a/Sources/FeatherOpenAPI/_Properties/AllowedValuesProperty.swift b/Sources/FeatherOpenAPI/_Properties/AllowedValuesProperty.swift new file mode 100644 index 0000000..0153485 --- /dev/null +++ b/Sources/FeatherOpenAPI/_Properties/AllowedValuesProperty.swift @@ -0,0 +1,18 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol AllowedValuesProperty { + associatedtype AllowedValuesPropertyType = AnyCodable + + var allowedValues: [AllowedValuesPropertyType]? { get } +} + +public extension AllowedValuesProperty { + var allowedValues: [AllowedValuesPropertyType]? { nil } +} diff --git a/Sources/FeatherOpenAPI/_Properties/DefaultValueProperty.swift b/Sources/FeatherOpenAPI/_Properties/DefaultValueProperty.swift new file mode 100644 index 0000000..234b390 --- /dev/null +++ b/Sources/FeatherOpenAPI/_Properties/DefaultValueProperty.swift @@ -0,0 +1,18 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol DefaultValueProperty { + associatedtype DefaultValuePropertyType = AnyCodable + + var defaultValue: DefaultValuePropertyType? { get } +} + +public extension DefaultValueProperty { + var defaultValue: DefaultValuePropertyType? { nil } +} diff --git a/Sources/FeatherOpenAPI/_Properties/DeprecatedProperty.swift b/Sources/FeatherOpenAPI/_Properties/DeprecatedProperty.swift new file mode 100644 index 0000000..8b24a9b --- /dev/null +++ b/Sources/FeatherOpenAPI/_Properties/DeprecatedProperty.swift @@ -0,0 +1,14 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +public protocol DeprecatedProperty { + var deprecated: Bool { get } +} + +public extension DeprecatedProperty { + var deprecated: Bool { false } +} diff --git a/Sources/FeatherOpenAPI/_Properties/DescriptionProperty.swift b/Sources/FeatherOpenAPI/_Properties/DescriptionProperty.swift new file mode 100644 index 0000000..143b394 --- /dev/null +++ b/Sources/FeatherOpenAPI/_Properties/DescriptionProperty.swift @@ -0,0 +1,14 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +public protocol DescriptionProperty { + var description: String? { get } +} + +public extension DescriptionProperty { + var description: String? { nil } +} diff --git a/Sources/FeatherOpenAPI/_Properties/ExampleProperty.swift b/Sources/FeatherOpenAPI/_Properties/ExampleProperty.swift new file mode 100644 index 0000000..7ea46b6 --- /dev/null +++ b/Sources/FeatherOpenAPI/_Properties/ExampleProperty.swift @@ -0,0 +1,18 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import OpenAPIKit30 + +public protocol ExampleProperty { + associatedtype ExamplePropertyType = AnyCodable + + var example: ExamplePropertyType? { get } +} + +public extension ExampleProperty { + var example: ExamplePropertyType? { nil } +} diff --git a/Sources/FeatherOpenAPI/_Properties/NullableProperty.swift b/Sources/FeatherOpenAPI/_Properties/NullableProperty.swift new file mode 100644 index 0000000..97cfc32 --- /dev/null +++ b/Sources/FeatherOpenAPI/_Properties/NullableProperty.swift @@ -0,0 +1,15 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + + +public protocol NullableProperty { + var nullable: Bool? { get } +} + +public extension NullableProperty { + var nullable: Bool? { nil } +} diff --git a/Sources/FeatherOpenAPI/_Properties/RequiredProperty.swift b/Sources/FeatherOpenAPI/_Properties/RequiredProperty.swift new file mode 100644 index 0000000..fa14aae --- /dev/null +++ b/Sources/FeatherOpenAPI/_Properties/RequiredProperty.swift @@ -0,0 +1,14 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +public protocol RequiredProperty { + var required: Bool { get } +} + +public extension RequiredProperty { + var required: Bool { true } +} diff --git a/Sources/FeatherOpenAPI/_Properties/TitleProperty.swift b/Sources/FeatherOpenAPI/_Properties/TitleProperty.swift new file mode 100644 index 0000000..392f826 --- /dev/null +++ b/Sources/FeatherOpenAPI/_Properties/TitleProperty.swift @@ -0,0 +1,14 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +public protocol TitleProperty { + var title: String? { get } +} + +public extension TitleProperty { + var title: String? { nil } +} diff --git a/Sources/FeatherOpenAPI/_Properties/VendorExtensionsProperty.swift b/Sources/FeatherOpenAPI/_Properties/VendorExtensionsProperty.swift new file mode 100644 index 0000000..e29ad2d --- /dev/null +++ b/Sources/FeatherOpenAPI/_Properties/VendorExtensionsProperty.swift @@ -0,0 +1,18 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol VendorExtensionsProperty { + var vendorExtensions: [String: AnyCodable] { get } +} + +public extension VendorExtensionsProperty { + var vendorExtensions: [String: AnyCodable] { [:] } +} + + diff --git a/Sources/feather-openapikit-generator/Entrypoint.swift b/Sources/feather-openapikit-generator/Entrypoint.swift deleted file mode 100644 index b453be2..0000000 --- a/Sources/feather-openapikit-generator/Entrypoint.swift +++ /dev/null @@ -1,71 +0,0 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else -import Foundation -#endif - -import SwiftParser -import SwiftSyntax - -enum BuilderError: Error { - case wrongArgumentsNumber - case invalidOutputFormat - case errorReadingContent(String) - case errorAccessContent(String) -} - -@main -struct Entrypoint { - - static func main() async throws { - print("feather-openapikit-generator is running...") - - guard CommandLine.arguments.count >= 3 else { - throw BuilderError.wrongArgumentsNumber - } - - let input = URL(fileURLWithPath: CommandLine.arguments[1]) - let output = URL(fileURLWithPath: CommandLine.arguments[2]) - let target = - CommandLine.arguments.count > 3 ? CommandLine.arguments[3] : "" - - let typeCollector = TypeCollector() - let typeList = try typeCollector.collectTypes(input.path) - let collectedTypes = typeList.joined(separator: ",\n ") - - let dateString = DateFormatter.localizedString( - from: Date(), - dateStyle: .medium, - timeStyle: .medium - ) - - let code = - """ - // generated on: \(dateString) - \(target == "FeatherOpenAPIKit" ? "" : "import FeatherOpenAPIKit") - - extension Component { - - public static func getComponentsOfType() -> [T] { - let prefixName = String(reflecting: self) + "." - return [ - \(collectedTypes) - ] - .compactMap { $0 as? T }.filter { - String(reflecting: $0).hasPrefix(prefixName) - } - } - } - """ - - print("Generated code path: \(output.path)") - - guard let data = code.data(using: .utf8) else { - throw BuilderError.invalidOutputFormat - } - - try data.write(to: output, options: .atomic) - - print("feather-openapikit-generator finished.") - } -} diff --git a/Sources/feather-openapikit-generator/TypeCollector.swift b/Sources/feather-openapikit-generator/TypeCollector.swift deleted file mode 100644 index 899db48..0000000 --- a/Sources/feather-openapikit-generator/TypeCollector.swift +++ /dev/null @@ -1,136 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 20.. -// - -#if canImport(FoundationEssentials) -import FoundationEssentials -#else -import Foundation -#endif - -import SwiftParser -import SwiftSyntax - -struct TypeCollector { - - func getFullName( - _ node: TypeSyntax - ) -> String { - node.trimmedDescription - } - - func getNodeName( - _ parentNodeName: String, - _ nodeName: String - ) -> String { - if parentNodeName.isEmpty { - return nodeName - } - return parentNodeName + "." + nodeName - } - - func collectTypes( - _ parentNodeName: String, - _ node: SyntaxProtocol - ) -> [String] { - var ret: [String] = [] - - var members: MemberBlockItemListSyntax? - var nodeName: String = "" - - if let extensionDecl = node.as(ExtensionDeclSyntax.self) { - nodeName = getNodeName( - parentNodeName, - getFullName(extensionDecl.extendedType) - ) - members = extensionDecl.memberBlock.members - } - else if let enumDecl = node.as(EnumDeclSyntax.self) { - nodeName = getNodeName(parentNodeName, enumDecl.name.text) - members = enumDecl.memberBlock.members - ret.append(nodeName + ".self") - } - else if let structDecl = node.as(StructDeclSyntax.self) { - nodeName = getNodeName(parentNodeName, structDecl.name.text) - members = structDecl.memberBlock.members - } - else if let classDecl = node.as(ClassDeclSyntax.self) { - nodeName = getNodeName(parentNodeName, classDecl.name.text) - members = classDecl.memberBlock.members - } - - if let _ = members { - for member in members! { - let list = collectTypes(nodeName, member.decl) - ret += list - } - } - - return ret - } - - func collectTypes( - _ root: SourceFileSyntax - ) -> [String] { - var ret: [String] = [] - - for st in root.statements { - let list = collectTypes("", st.item) - ret += list - } - - return ret - } - - func collectTypes( - _ dirPath: String - ) throws -> [String] { - var ret: [String] = [] - - let fileManager = FileManager.default - let folderURL = URL(fileURLWithPath: dirPath) - - do { - let fileURLs = try fileManager.contentsOfDirectory( - at: folderURL, - includingPropertiesForKeys: nil, - options: [.skipsHiddenFiles] - ) - - for fileURL in fileURLs { - if fileURL.hasDirectoryPath { - let list = try collectTypes(fileURL.path) - ret += list - } - else if fileURL.pathExtension == "swift" { - do { - let fileContent = try String( - contentsOf: fileURL, - encoding: .utf8 - ) - let list = collectTypes( - Parser.parse(source: fileContent) - ) - ret += list - } - catch { - throw BuilderError.errorReadingContent( - "Error reading content of \(fileURL.relativePath): \(error)" - ) - } - } - } - } - catch { - throw BuilderError.errorAccessContent( - "Error accessing contents of the folder: \(error)" - ) - } - - return ret - } - -} diff --git a/Tests/FeatherOpenAPITests/Example/TestObjects.swift b/Tests/FeatherOpenAPITests/Example/TestObjects.swift index 1691023..02a41de 100644 --- a/Tests/FeatherOpenAPITests/Example/TestObjects.swift +++ b/Tests/FeatherOpenAPITests/Example/TestObjects.swift @@ -27,10 +27,18 @@ public struct ExampleFieldName: StringSchemaRepresentable { struct TodoIDField: IntSchemaRepresentable { var example: Int? { 1 } +// var openAPIIdentifier: String { "foo_\(example)" } +// var openAPIIdentifierSuffix: String { String(example) } + +// init(example: Int? = 1) { +// self.example = example +// } } struct TodoTitleField: StringSchemaRepresentable { - var example: String? { "Buy milk" } + var example: String? = "Buy milk" + + } struct TodoIsCompleteField: BoolSchemaRepresentable { @@ -41,12 +49,19 @@ struct TodoDetailObject: ObjectSchemaRepresentable { var propertyMap: SchemaMap { [ - "id": TodoIDField(), +// "id1": TodoIDField(example: 1).referenced(id: "foo"), +// "id2": TodoIDField(example: 2).referenced(id: "bar"), +// "id3": TodoIDField() +// .inlined(example: 2) +// .inlined(example: 123) +// .inlined(), -> Self +// .referenced() -> .component(named: "") "title": TodoTitleField(), "isComplete": TodoIsCompleteField(), // TODO: move required to schema -> use that to reference - "foo": SchemaReference(TodoIDField(), required: false), -// "foo": TodoIDField().reference(id: "lorem"), + "bar": SchemaReference(TodoIDField(), required: false), +// "foo": TodoIDField(example: 12).referenced(id: ""), +// "unsafe": UnsafeSchemaReference("asdf"), ] } @@ -56,8 +71,7 @@ struct TodoCreateRequestBody: RequestBodyRepresentable { var contentMap: ContentMap { [ - : -// .json: TodoDetailObject() + .json: Content(TodoDetailObject()), ] } } diff --git a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift index 5bddeb2..c93ac4e 100644 --- a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift +++ b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift @@ -14,6 +14,28 @@ import Testing @testable import FeatherOpenAPI + +struct MyInfo: InfoRepresentable { + var title: String { "foo" } + var version: String { "1.0.0" } +} + +struct MyDocument: DocumentRepresentable { + var info: OpenAPIInfoRepresentable + var paths: PathMap + var components: OpenAPIComponentsRepresentable + + init( + info: OpenAPIInfoRepresentable, + paths: PathMap, + components: OpenAPIComponentsRepresentable + ) { + self.info = info + self.paths = paths + self.components = components + } +} + @Suite struct FeatherOpenAPIKitTests { @@ -24,19 +46,18 @@ struct FeatherOpenAPIKitTests { var pathMap: PathMap { [ - "todos": TodoPathItems() + "todos": TodoPathItems(), +// "laci": LaciPathItems(), ] } } let collection = MyPathCollection() +// collection.components.schemas.register(id: "", TodoFieldId()) - let document = Document( - info: Info( - title: "foo", - version: "1.0.0" - ), - paths: collection.pathMap.mapValues { $0.openAPIPathItem() }, + let document = MyDocument( + info: MyInfo(), + paths: collection.pathMap, components: collection.components ) @@ -52,15 +73,15 @@ struct FeatherOpenAPIKitTests { print("---- 3.0 ----") print(result) - let doc31 = openAPIdoc.convert(to: .v3_1_0) - let result31 = try encoder.encode(doc31) - print("---- 3.1 ----") - print(result31) - - let doc32 = openAPIdoc.convert(to: .v3_2_0) - let result32 = try encoder.encode(doc32) - print("---- 3.2 ----") - print(result32) +// let doc31 = openAPIdoc.convert(to: .v3_1_0) +// let result31 = try encoder.encode(doc31) +// print("---- 3.1 ----") +// print(result31) +// +// let doc32 = openAPIdoc.convert(to: .v3_2_0) +// let result32 = try encoder.encode(doc32) +// print("---- 3.2 ----") +// print(result32) } } From ba6887000ae8f935cf29ddd08f75f30573525df2 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Fri, 23 Jan 2026 22:19:22 +0100 Subject: [PATCH 16/34] parameter references --- .../Components/ComponentsRepresentable.swift | 4 +- Sources/FeatherOpenAPI/Header/HeaderMap.swift | 2 +- .../Header/HeaderRepresentable.swift | 12 +++++- .../Operation/OperationRepresentable.swift | 39 ++++++++++++------ .../OrderedDictionary+Composition.swift | 15 +++++++ .../OpenAPIParameterRepresentable.swift | 6 +-- .../Parameter/Abstraction/ParameterID.swift | 2 +- .../Abstraction/ParameterRepresentable.swift | 30 ++++++++++---- .../PathCollectionRepresentable.swift | 17 +++++++- .../PathItem/PathItemRepresentable.swift | 37 ++++++++++++----- .../ParameterReferenceRepresentable.swift | 40 +++++++++++++++++++ .../ReferencedSchemaMapRepresentable.swift | 5 +++ .../SchemaReference.swift | 0 .../RequestBodyRepresentable.swift | 18 +++++++-- .../OrderedDictionary+Composition.swift | 19 --------- .../Example/TestObjects.swift | 16 +++++++- 16 files changed, 201 insertions(+), 61 deletions(-) create mode 100644 Sources/FeatherOpenAPI/OrderedDictionary+Composition.swift create mode 100644 Sources/FeatherOpenAPI/Reference/ParameterReferenceRepresentable.swift rename Sources/FeatherOpenAPI/{Schema/Abstraction => Reference}/SchemaReference.swift (100%) delete mode 100644 Sources/FeatherOpenAPIKit/Extensions/OrderedDictionary+Composition.swift diff --git a/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift b/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift index 34511d0..8a6f40a 100644 --- a/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift +++ b/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift @@ -57,7 +57,9 @@ public extension ComponentsRepresentable { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in parameters { - result[.init(stringLiteral: key.rawValue)] = value.openAPIParameter() + if let parameter = value.openAPIParameter().b { + result[.init(stringLiteral: key.rawValue)] = parameter + } } return result } diff --git a/Sources/FeatherOpenAPI/Header/HeaderMap.swift b/Sources/FeatherOpenAPI/Header/HeaderMap.swift index 1fe03b6..db2ebcf 100644 --- a/Sources/FeatherOpenAPI/Header/HeaderMap.swift +++ b/Sources/FeatherOpenAPI/Header/HeaderMap.swift @@ -10,6 +10,6 @@ import OpenAPIKit30 public typealias HeaderMap = OrderedDictionary < String, - OpenAPIHeaderRepresentable + HeaderRepresentable > diff --git a/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift b/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift index 2ae4ce6..e6d27ff 100644 --- a/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift +++ b/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift @@ -12,7 +12,9 @@ public protocol HeaderRepresentable: DescriptionProperty, RequiredProperty, DeprecatedProperty, - VendorExtensionsProperty + VendorExtensionsProperty, + // reference + ReferencedSchemaMapRepresentable { var schema: OpenAPISchemaRepresentable { get } } @@ -28,4 +30,12 @@ public extension HeaderRepresentable { vendorExtensions: vendorExtensions ) } + + var referencedSchemaMap: OrderedDictionary { + var results = OrderedDictionary() + if let ref = schema as? SchemaReferenceRepresentable { + results[ref.id] = ref.object + } + return results + } } diff --git a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift index aa9e0b6..1ed4359 100644 --- a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift +++ b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift @@ -10,10 +10,12 @@ import OpenAPIKit30 public protocol OperationRepresentable: OpenAPIOperationRepresentable, - ReferencedSchemaMapRepresentable, + // properties DescriptionProperty, DeprecatedProperty, - VendorExtensionsProperty + VendorExtensionsProperty, + // references + ReferencedSchemaMapRepresentable { // associatedtype RequestBodyType: RequestBodyRepresentable @@ -45,7 +47,7 @@ public extension OperationRepresentable { description: description, externalDocs: nil, operationId: operationId, - parameters: parameters.map { .init($0.openAPIParameter()) }, + parameters: parameters.map { $0.openAPIParameter() }, requestBody: requestBody?.openAPIRequestBody(), responses: responseMap.mapValues { .init($0.openAPIResponse()) }, callbacks: [:], @@ -56,33 +58,44 @@ public extension OperationRepresentable { ) } + var referencedParameterMap: OrderedDictionary { + var results = OrderedDictionary() + + for parameter in parameters { + if let ref = parameter as? ParameterReferenceRepresentable { + if case let .b(parameter) = ref.object.openAPIParameter() { + results[ref.id] = parameter + } + } + } + return results + } + var referencedSchemaMap: OrderedDictionary { var results = OrderedDictionary() for parameter in parameters { - if let ref = parameter.schema as? SchemaReferenceRepresentable { - results[ref.id] = ref.object - } + results.merge(parameter.referencedSchemaMap) } - for content in requestBody?.contentMap.values ?? [] { - if let ref = content.schema as? SchemaReferenceRepresentable { - results[ref.id] = ref.object - } + if let schemaMap = requestBody?.referencedSchemaMap { + results.merge(schemaMap) } let headers = responseMap.values .map { $0.headerMap.values } .flatMap { $0 } + for header in headers { + results.merge(header.referencedSchemaMap) + } + let contents = responseMap.values .map { $0.contentMap.values } .flatMap { $0 } for content in contents { - for (k, v) in content.referencedSchemaMap { - results[k] = v - } + results.merge(content.referencedSchemaMap) } return results diff --git a/Sources/FeatherOpenAPI/OrderedDictionary+Composition.swift b/Sources/FeatherOpenAPI/OrderedDictionary+Composition.swift new file mode 100644 index 0000000..cdab2b5 --- /dev/null +++ b/Sources/FeatherOpenAPI/OrderedDictionary+Composition.swift @@ -0,0 +1,15 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 10/01/2024. +// + +import OpenAPIKit30 + +extension OrderedDictionary { + + mutating func merge(_ other: Self) { + merge(other, uniquingKeysWith: { _, new in new }) + } +} diff --git a/Sources/FeatherOpenAPI/Parameter/Abstraction/OpenAPIParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/Abstraction/OpenAPIParameterRepresentable.swift index 95ea1a0..602cc30 100644 --- a/Sources/FeatherOpenAPI/Parameter/Abstraction/OpenAPIParameterRepresentable.swift +++ b/Sources/FeatherOpenAPI/Parameter/Abstraction/OpenAPIParameterRepresentable.swift @@ -8,12 +8,12 @@ import OpenAPIKit30 public protocol OpenAPIParameterRepresentable { - func openAPIParameter() -> OpenAPI.Parameter + func openAPIParameter() -> Either, OpenAPI.Parameter> } extension OpenAPI.Parameter: OpenAPIParameterRepresentable { - public func openAPIParameter() -> OpenAPI.Parameter { - self + public func openAPIParameter() -> Either, OpenAPI.Parameter> { + .init(self) } } diff --git a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift index 3bb5245..f561664 100644 --- a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift +++ b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 23.. // -public struct ParameterID: Hashable { +public struct ParameterID: Sendable, Hashable { public var rawValue: String diff --git a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift index 5fff43f..2ff7745 100644 --- a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift +++ b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift @@ -9,9 +9,13 @@ import OpenAPIKit30 public protocol ParameterRepresentable: OpenAPIParameterRepresentable, + Identifiable, + // property DescriptionProperty, DeprecatedProperty, - VendorExtensionsProperty + VendorExtensionsProperty, + // reference + ReferencedSchemaMapRepresentable { var name: String { get } var context: OpenAPI.Parameter.Context { get } @@ -21,14 +25,24 @@ public protocol ParameterRepresentable: public extension ParameterRepresentable { - func openAPIParameter() -> OpenAPI.Parameter { + func openAPIParameter() -> Either, OpenAPI.Parameter> { .init( - name: name, - context: context, - schema: schema.openAPISchema(), - description: description, - deprecated: deprecated, - vendorExtensions: vendorExtensions + .init( + name: name, + context: context, + schema: schema.openAPISchema(), + description: description, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) ) } + + var referencedSchemaMap: OrderedDictionary { + var results = OrderedDictionary() + if let ref = schema as? SchemaReferenceRepresentable { + results[ref.id] = ref.object + } + return results + } } diff --git a/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift b/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift index a1416bc..5e7762f 100644 --- a/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift +++ b/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift @@ -8,7 +8,8 @@ import OpenAPIKit30 public protocol PathCollectionRepresentable: - ReferencedSchemaMapRepresentable + ReferencedSchemaMapRepresentable, + ReferencedParameterMapRepresentable { var pathMap: PathMap { get } var components: Components { get } @@ -28,10 +29,24 @@ extension PathCollectionRepresentable { } return results } + + var referencedParameterMap: OrderedDictionary { + var results = OrderedDictionary() + + let parameterMaps = pathMap.values + .map { $0.referencedParameterMap } + .flatMap { $0 } + + for (k, v) in parameterMaps { + results[k] = v + } + return results + } var components: Components { .init( schemas: referencedSchemaMap.mapValues { $0.openAPISchema() }, + parameters: referencedParameterMap.mapValues { $0.openAPIParameter().b }.compactMapValues { $0 } ) } } diff --git a/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift b/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift index 9851368..c0d8095 100644 --- a/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift +++ b/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift @@ -9,9 +9,12 @@ import OpenAPIKit30 public protocol PathItemRepresentable: OpenAPIPathItemRepresentable, - ReferencedSchemaMapRepresentable, + // properties DescriptionProperty, - VendorExtensionsProperty + VendorExtensionsProperty, + // reference + ReferencedSchemaMapRepresentable, + ReferencedParameterMapRepresentable { var summary: String? { get } @@ -58,10 +61,8 @@ public extension PathItemRepresentable { ) } - var referencedSchemaMap: OrderedDictionary { - var results = OrderedDictionary() - - let schemaMaps = [ + var allOperations: [OperationRepresentable] { + [ get, put, post, @@ -69,10 +70,28 @@ public extension PathItemRepresentable { options, head, patch, - trace - ].compactMap { $0 }.map { $0.referencedSchemaMap }.flatMap { $0 } + trace, + ] + .compactMap { $0 } + } + + var referencedSchemaMap: OrderedDictionary { + var results = OrderedDictionary() + + let maps = allOperations.map { $0.referencedSchemaMap }.flatMap { $0 } + + for (k, v) in maps { + results[k] = v + } + return results + } + + var referencedParameterMap: OrderedDictionary { + var results = OrderedDictionary() + + let maps = allOperations.map { $0.referencedParameterMap }.flatMap { $0 } - for (k, v) in schemaMaps { + for (k, v) in maps { results[k] = v } return results diff --git a/Sources/FeatherOpenAPI/Reference/ParameterReferenceRepresentable.swift b/Sources/FeatherOpenAPI/Reference/ParameterReferenceRepresentable.swift new file mode 100644 index 0000000..a285b46 --- /dev/null +++ b/Sources/FeatherOpenAPI/Reference/ParameterReferenceRepresentable.swift @@ -0,0 +1,40 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol ParameterReferenceRepresentable { + var id: ParameterID { get } + var object: ParameterRepresentable { get } +} + +public struct ParameterReference: + ParameterRepresentable, + ParameterReferenceRepresentable +{ + public var name: String { object.name } + public var context: OpenAPIKit30.OpenAPI.Parameter.Context { object.context } + public var schema: any OpenAPISchemaRepresentable { object.schema } + + public var object: ParameterRepresentable { + _object + } + + public var id: ParameterID + public var _object: T + + public init( + _ object: T + ) { + self.id = .init(object.openAPIIdentifier) + self._object = object + } + + public func openAPIParameter() -> Either, OpenAPI.Parameter> { + .reference(.component(named: id.rawValue)) + } +} diff --git a/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift b/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift index e94edf9..ea20f28 100644 --- a/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift +++ b/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift @@ -10,3 +10,8 @@ import OpenAPIKit30 public protocol ReferencedSchemaMapRepresentable { var referencedSchemaMap: OrderedDictionary { get } } + + +public protocol ReferencedParameterMapRepresentable { + var referencedParameterMap: OrderedDictionary { get } +} diff --git a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaReference.swift b/Sources/FeatherOpenAPI/Reference/SchemaReference.swift similarity index 100% rename from Sources/FeatherOpenAPI/Schema/Abstraction/SchemaReference.swift rename to Sources/FeatherOpenAPI/Reference/SchemaReference.swift diff --git a/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyRepresentable.swift index fa13982..c0e8d1b 100644 --- a/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyRepresentable.swift +++ b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyRepresentable.swift @@ -12,14 +12,16 @@ public protocol RequestBodyRepresentable: OpenAPIRequestBodyRepresentable, DescriptionProperty, RequiredProperty, - VendorExtensionsProperty + VendorExtensionsProperty, + // reference + ReferencedSchemaMapRepresentable { var contentMap: ContentMap { get } } -extension RequestBodyRepresentable { +public extension RequestBodyRepresentable { - public func openAPIRequestBody() -> OpenAPI.Request { + func openAPIRequestBody() -> OpenAPI.Request { .init( description: description, content: contentMap.mapValues { $0.openAPIContent() }, @@ -27,4 +29,14 @@ extension RequestBodyRepresentable { vendorExtensions: vendorExtensions ) } + + var referencedSchemaMap: OrderedDictionary { + var results = OrderedDictionary() + for content in contentMap.values { + if let ref = content.schema as? SchemaReferenceRepresentable { + results[ref.id] = ref.object + } + } + return results + } } diff --git a/Sources/FeatherOpenAPIKit/Extensions/OrderedDictionary+Composition.swift b/Sources/FeatherOpenAPIKit/Extensions/OrderedDictionary+Composition.swift deleted file mode 100644 index fc8767e..0000000 --- a/Sources/FeatherOpenAPIKit/Extensions/OrderedDictionary+Composition.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 10/01/2024. -// - -import OpenAPIKit30 - -public extension OrderedDictionary { - - static func + (lhs: Self, rhs: Self) -> Self { - var result = lhs - for key in rhs.keys { - result[key] = rhs[key] - } - return result - } -} diff --git a/Tests/FeatherOpenAPITests/Example/TestObjects.swift b/Tests/FeatherOpenAPITests/Example/TestObjects.swift index 02a41de..a1d9413 100644 --- a/Tests/FeatherOpenAPITests/Example/TestObjects.swift +++ b/Tests/FeatherOpenAPITests/Example/TestObjects.swift @@ -59,7 +59,7 @@ struct TodoDetailObject: ObjectSchemaRepresentable { "title": TodoTitleField(), "isComplete": TodoIsCompleteField(), // TODO: move required to schema -> use that to reference - "bar": SchemaReference(TodoIDField(), required: false), +// "bar": SchemaReference(TodoIDField(), required: false), // "foo": TodoIDField(example: 12).referenced(id: ""), // "unsafe": UnsafeSchemaReference("asdf"), ] @@ -81,9 +81,23 @@ struct TodoCreateResponse: JSONResponseRepresentable { var schema = TodoDetailObject() } +struct TodoIdParameter: ParameterRepresentable { + + var name: String { "todoId" } + var context: OpenAPIKit30.OpenAPI.Parameter.Context { .path } + var schema: any FeatherOpenAPI.OpenAPISchemaRepresentable { SchemaReference(TodoIDField()) } +} + struct TodoCreateOperation: OperationRepresentable { + var parameters: [ParameterRepresentable] { + [ +// TodoIdParameter(), + ParameterReference(TodoIdParameter()), + ] + } + var requestBody = TodoCreateRequestBody() var responseMap: ResponseMap { [ From 810e09405fb9103fa7044df48814a77e9796bcab Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Fri, 23 Jan 2026 23:01:58 +0100 Subject: [PATCH 17/34] request body ref --- .../FeatherOpenAPI/Callback/CallbackID.swift | 2 +- .../Components/ComponentsRepresentable.swift | 5 ++- .../FeatherOpenAPI/Example/ExampleID.swift | 2 +- Sources/FeatherOpenAPI/Header/HeaderID.swift | 2 +- Sources/FeatherOpenAPI/Link/LinkID.swift | 2 +- .../Operation/OperationRepresentable.swift | 29 +++++++++++++- .../Parameter/Abstraction/ParameterID.swift | 2 +- .../Abstraction/ParameterRepresentable.swift | 4 ++ .../PathCollectionRepresentable.swift | 19 +++++++++- .../PathItem/PathItemRepresentable.swift | 14 ++++++- .../ParameterReferenceRepresentable.swift | 2 +- .../ReferencedSchemaMapRepresentable.swift | 4 ++ .../RequestBodyReferenceRepresentable.swift | 38 +++++++++++++++++++ .../Reference/SchemaReference.swift | 4 +- .../OpenAPIRequestBodyRepresentable.swift | 7 ++-- .../Abstraction/RequestBodyID.swift | 2 +- .../RequestBodyRepresentable.swift | 16 +++++--- .../Response/Abstraction/ResponseID.swift | 2 +- .../Schema/Abstraction/SchemaID.swift | 2 +- .../Abstraction/SchemaRepresentable.swift | 4 ++ .../SecurityScheme/SecuritySchemeID.swift | 2 +- .../Example/TestObjects.swift | 8 ++-- 22 files changed, 140 insertions(+), 32 deletions(-) create mode 100644 Sources/FeatherOpenAPI/Reference/RequestBodyReferenceRepresentable.swift diff --git a/Sources/FeatherOpenAPI/Callback/CallbackID.swift b/Sources/FeatherOpenAPI/Callback/CallbackID.swift index f714cad..d1625b7 100644 --- a/Sources/FeatherOpenAPI/Callback/CallbackID.swift +++ b/Sources/FeatherOpenAPI/Callback/CallbackID.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 23.. // -public struct CallbackID: Hashable { +public struct CallbackID: Sendable, Equatable, Hashable, Codable { public var rawValue: String diff --git a/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift b/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift index 8a6f40a..0618096 100644 --- a/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift +++ b/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift @@ -87,8 +87,9 @@ public extension ComponentsRepresentable { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in requestBodies { - result[.init(stringLiteral: key.rawValue)] = - value.openAPIRequestBody() + if let requestBody = value.openAPIRequestBody().b { + result[.init(stringLiteral: key.rawValue)] = requestBody + } } return result } diff --git a/Sources/FeatherOpenAPI/Example/ExampleID.swift b/Sources/FeatherOpenAPI/Example/ExampleID.swift index e89e013..11bed6b 100644 --- a/Sources/FeatherOpenAPI/Example/ExampleID.swift +++ b/Sources/FeatherOpenAPI/Example/ExampleID.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 23.. // -public struct ExampleID: Hashable { +public struct ExampleID: Sendable, Equatable, Hashable, Codable { public var rawValue: String diff --git a/Sources/FeatherOpenAPI/Header/HeaderID.swift b/Sources/FeatherOpenAPI/Header/HeaderID.swift index 1fcbdb1..198b931 100644 --- a/Sources/FeatherOpenAPI/Header/HeaderID.swift +++ b/Sources/FeatherOpenAPI/Header/HeaderID.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 23.. // -public struct HeaderID: Hashable { +public struct HeaderID: Sendable, Equatable, Hashable, Codable { public var rawValue: String diff --git a/Sources/FeatherOpenAPI/Link/LinkID.swift b/Sources/FeatherOpenAPI/Link/LinkID.swift index a17abea..b8d1c9e 100644 --- a/Sources/FeatherOpenAPI/Link/LinkID.swift +++ b/Sources/FeatherOpenAPI/Link/LinkID.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 23.. // -public struct LinkID: Hashable { +public struct LinkID: Sendable, Equatable, Hashable, Codable { public var rawValue: String diff --git a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift index 1ed4359..f1420a5 100644 --- a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift +++ b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift @@ -41,14 +41,30 @@ public extension OperationRepresentable { var requestBody: RequestBodyRepresentable? { nil } func openAPIOperation() -> OpenAPI.Operation { - .init( + if let requestBody { + return .init( + tags: nil, + summary: summary, + description: description, + externalDocs: nil, + operationId: operationId, + parameters: parameters.map { $0.openAPIParameter() }, + requestBody: requestBody.openAPIRequestBody(), + responses: responseMap.mapValues { .init($0.openAPIResponse()) }, + callbacks: [:], + deprecated: deprecated, + security: nil, + servers: nil, + vendorExtensions: vendorExtensions + ) + } + return .init( tags: nil, summary: summary, description: description, externalDocs: nil, operationId: operationId, parameters: parameters.map { $0.openAPIParameter() }, - requestBody: requestBody?.openAPIRequestBody(), responses: responseMap.mapValues { .init($0.openAPIResponse()) }, callbacks: [:], deprecated: deprecated, @@ -70,6 +86,15 @@ public extension OperationRepresentable { } return results } + + var referencedRequestBodyMap: OrderedDictionary { + var results = OrderedDictionary() + + if let ref = requestBody as? RequestBodyReferenceRepresentable { + results[ref.id] = ref.object + } + return results + } var referencedSchemaMap: OrderedDictionary { var results = OrderedDictionary() diff --git a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift index f561664..12f3e32 100644 --- a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift +++ b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 23.. // -public struct ParameterID: Sendable, Hashable { +public struct ParameterID: Sendable, Equatable, Hashable, Codable { public var rawValue: String diff --git a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift index 2ff7745..6685a58 100644 --- a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift +++ b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift @@ -25,6 +25,10 @@ public protocol ParameterRepresentable: public extension ParameterRepresentable { + func reference() -> ParameterReference { + .init(self) + } + func openAPIParameter() -> Either, OpenAPI.Parameter> { .init( .init( diff --git a/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift b/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift index 5e7762f..c1a2c0a 100644 --- a/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift +++ b/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift @@ -9,7 +9,8 @@ import OpenAPIKit30 public protocol PathCollectionRepresentable: ReferencedSchemaMapRepresentable, - ReferencedParameterMapRepresentable + ReferencedParameterMapRepresentable, + ReferencedRequestBodyMapRepresentable { var pathMap: PathMap { get } var components: Components { get } @@ -43,10 +44,24 @@ extension PathCollectionRepresentable { return results } + var referencedRequestBodyMap: OrderedDictionary { + var results = OrderedDictionary() + + let requestBodyMaps = pathMap.values + .map { $0.referencedRequestBodyMap } + .flatMap { $0 } + + for (k, v) in requestBodyMaps { + results[k] = v + } + return results + } + var components: Components { .init( schemas: referencedSchemaMap.mapValues { $0.openAPISchema() }, - parameters: referencedParameterMap.mapValues { $0.openAPIParameter().b }.compactMapValues { $0 } + parameters: referencedParameterMap.mapValues { $0.openAPIParameter().b }.compactMapValues { $0 }, + requestBodies: referencedRequestBodyMap.mapValues { $0.openAPIRequestBody().b }.compactMapValues { $0 } ) } } diff --git a/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift b/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift index c0d8095..6af42ab 100644 --- a/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift +++ b/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift @@ -14,7 +14,8 @@ public protocol PathItemRepresentable: VendorExtensionsProperty, // reference ReferencedSchemaMapRepresentable, - ReferencedParameterMapRepresentable + ReferencedParameterMapRepresentable, + ReferencedRequestBodyMapRepresentable { var summary: String? { get } @@ -96,4 +97,15 @@ public extension PathItemRepresentable { } return results } + + var referencedRequestBodyMap: OrderedDictionary { + var results = OrderedDictionary() + + let maps = allOperations.map { $0.referencedRequestBodyMap }.flatMap { $0 } + + for (k, v) in maps { + results[k] = v + } + return results + } } diff --git a/Sources/FeatherOpenAPI/Reference/ParameterReferenceRepresentable.swift b/Sources/FeatherOpenAPI/Reference/ParameterReferenceRepresentable.swift index a285b46..30f14c1 100644 --- a/Sources/FeatherOpenAPI/Reference/ParameterReferenceRepresentable.swift +++ b/Sources/FeatherOpenAPI/Reference/ParameterReferenceRepresentable.swift @@ -27,7 +27,7 @@ public struct ParameterReference: public var id: ParameterID public var _object: T - public init( + internal init( _ object: T ) { self.id = .init(object.openAPIIdentifier) diff --git a/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift b/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift index ea20f28..90ed963 100644 --- a/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift +++ b/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift @@ -15,3 +15,7 @@ public protocol ReferencedSchemaMapRepresentable { public protocol ReferencedParameterMapRepresentable { var referencedParameterMap: OrderedDictionary { get } } + +public protocol ReferencedRequestBodyMapRepresentable { + var referencedRequestBodyMap: OrderedDictionary { get } +} diff --git a/Sources/FeatherOpenAPI/Reference/RequestBodyReferenceRepresentable.swift b/Sources/FeatherOpenAPI/Reference/RequestBodyReferenceRepresentable.swift new file mode 100644 index 0000000..5fa1991 --- /dev/null +++ b/Sources/FeatherOpenAPI/Reference/RequestBodyReferenceRepresentable.swift @@ -0,0 +1,38 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol RequestBodyReferenceRepresentable { + var id: RequestBodyID { get } + var object: RequestBodyRepresentable { get } +} + +public struct RequestBodyReference: + RequestBodyRepresentable, + RequestBodyReferenceRepresentable +{ + public var contentMap: ContentMap { object.contentMap } + + public var object: RequestBodyRepresentable { + _object + } + + public var id: RequestBodyID + public var _object: T + + internal init( + _ object: T + ) { + self.id = .init(object.openAPIIdentifier) + self._object = object + } + + public func openAPIRequestBody() -> Either, OpenAPI.Request> { + .reference(.component(named: id.rawValue)) + } +} diff --git a/Sources/FeatherOpenAPI/Reference/SchemaReference.swift b/Sources/FeatherOpenAPI/Reference/SchemaReference.swift index 893637d..a6a6afd 100644 --- a/Sources/FeatherOpenAPI/Reference/SchemaReference.swift +++ b/Sources/FeatherOpenAPI/Reference/SchemaReference.swift @@ -13,7 +13,7 @@ public protocol SchemaReferenceRepresentable { } public struct SchemaReference: - OpenAPISchemaRepresentable, + SchemaRepresentable, SchemaReferenceRepresentable { public var object: any SchemaRepresentable { @@ -24,7 +24,7 @@ public struct SchemaReference: public var _object: T public var required: Bool - public init( + internal init( _ object: T, required: Bool = true ) { diff --git a/Sources/FeatherOpenAPI/RequestBody/Abstraction/OpenAPIRequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/Abstraction/OpenAPIRequestBodyRepresentable.swift index 7290514..0192ad2 100644 --- a/Sources/FeatherOpenAPI/RequestBody/Abstraction/OpenAPIRequestBodyRepresentable.swift +++ b/Sources/FeatherOpenAPI/RequestBody/Abstraction/OpenAPIRequestBodyRepresentable.swift @@ -8,13 +8,12 @@ import OpenAPIKit30 public protocol OpenAPIRequestBodyRepresentable { - func openAPIRequestBody() -> OpenAPI.Request + func openAPIRequestBody() -> Either, OpenAPI.Request> } extension OpenAPI.Request: OpenAPIRequestBodyRepresentable { - public func openAPIRequestBody() -> OpenAPI.Request { - self + public func openAPIRequestBody() -> Either, OpenAPI.Request> { + .init(self) } } - diff --git a/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyID.swift b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyID.swift index a486a96..7daa82d 100644 --- a/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyID.swift +++ b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyID.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -public struct RequestBodyID: Hashable { +public struct RequestBodyID: Sendable, Equatable, Hashable, Codable { public var rawValue: String diff --git a/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyRepresentable.swift index c0e8d1b..9591a2b 100644 --- a/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyRepresentable.swift +++ b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyRepresentable.swift @@ -20,13 +20,19 @@ public protocol RequestBodyRepresentable: } public extension RequestBodyRepresentable { + + func reference() -> RequestBodyReference { + .init(self) + } - func openAPIRequestBody() -> OpenAPI.Request { + func openAPIRequestBody() -> Either, OpenAPI.Request> { .init( - description: description, - content: contentMap.mapValues { $0.openAPIContent() }, - required: `required`, - vendorExtensions: vendorExtensions + .init( + description: description, + content: contentMap.mapValues { $0.openAPIContent() }, + required: `required`, + vendorExtensions: vendorExtensions + ) ) } diff --git a/Sources/FeatherOpenAPI/Response/Abstraction/ResponseID.swift b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseID.swift index fef7c07..05cfd08 100644 --- a/Sources/FeatherOpenAPI/Response/Abstraction/ResponseID.swift +++ b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseID.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -public struct ResponseID: Hashable { +public struct ResponseID: Sendable, Equatable, Hashable, Codable { public var rawValue: String diff --git a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaID.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaID.swift index 4643e0e..0349d58 100644 --- a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaID.swift +++ b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaID.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -public struct SchemaID: Hashable { +public struct SchemaID: Sendable, Equatable, Hashable, Codable { public var rawValue: String diff --git a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift index 264c40c..45d7642 100644 --- a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift @@ -22,6 +22,10 @@ public protocol SchemaRepresentable: public extension SchemaRepresentable { + func reference() -> SchemaReference { + .init(self) + } + var deprecated: Bool? { nil } var referencedSchemaMap: OrderedDictionary { diff --git a/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeID.swift b/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeID.swift index 9a958b3..2d48df4 100644 --- a/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeID.swift +++ b/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeID.swift @@ -5,7 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 23.. // -public struct SecuritySchemeID: Hashable { +public struct SecuritySchemeID: Sendable, Equatable, Hashable, Codable { public var rawValue: String diff --git a/Tests/FeatherOpenAPITests/Example/TestObjects.swift b/Tests/FeatherOpenAPITests/Example/TestObjects.swift index a1d9413..d0b388b 100644 --- a/Tests/FeatherOpenAPITests/Example/TestObjects.swift +++ b/Tests/FeatherOpenAPITests/Example/TestObjects.swift @@ -71,7 +71,7 @@ struct TodoCreateRequestBody: RequestBodyRepresentable { var contentMap: ContentMap { [ - .json: Content(TodoDetailObject()), + .json: Content(TodoDetailObject().reference()), ] } } @@ -85,7 +85,7 @@ struct TodoIdParameter: ParameterRepresentable { var name: String { "todoId" } var context: OpenAPIKit30.OpenAPI.Parameter.Context { .path } - var schema: any FeatherOpenAPI.OpenAPISchemaRepresentable { SchemaReference(TodoIDField()) } + var schema: any FeatherOpenAPI.OpenAPISchemaRepresentable { TodoIDField().reference() } } @@ -94,11 +94,11 @@ struct TodoCreateOperation: OperationRepresentable { var parameters: [ParameterRepresentable] { [ // TodoIdParameter(), - ParameterReference(TodoIdParameter()), + TodoIdParameter().reference(), ] } - var requestBody = TodoCreateRequestBody() + var requestBody: RequestBodyRepresentable? = TodoCreateRequestBody().reference() var responseMap: ResponseMap { [ 200: TodoCreateResponse(), From 87c83b969ee7df13e197e9c8bae5be15c27be935 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Fri, 23 Jan 2026 23:16:38 +0100 Subject: [PATCH 18/34] header references --- .../Components/ComponentsRepresentable.swift | 4 +- .../Header/HeaderRepresentable.swift | 19 +++++++--- .../Header/OpenAPIHeaderRepresentable.swift | 6 +-- .../Operation/OperationRepresentable.swift | 22 ++++++++++- .../PathCollectionRepresentable.swift | 19 +++++++++- .../PathItem/PathItemRepresentable.swift | 14 ++++++- .../HeaderReferenceRepresentable.swift | 38 +++++++++++++++++++ .../ReferencedSchemaMapRepresentable.swift | 4 ++ .../Abstraction/ResponseRepresentable.swift | 2 +- .../Example/TestObjects.swift | 11 ++++++ 10 files changed, 124 insertions(+), 15 deletions(-) create mode 100644 Sources/FeatherOpenAPI/Reference/HeaderReferenceRepresentable.swift diff --git a/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift b/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift index 0618096..3d8eed0 100644 --- a/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift +++ b/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift @@ -98,7 +98,9 @@ public extension ComponentsRepresentable { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in headers { - result[.init(stringLiteral: key.rawValue)] = value.openAPIHeader() + if let header = value.openAPIHeader().b { + result[.init(stringLiteral: key.rawValue)] = header + } } return result } diff --git a/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift b/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift index e6d27ff..328ade8 100644 --- a/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift +++ b/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift @@ -9,6 +9,7 @@ import OpenAPIKit30 public protocol HeaderRepresentable: OpenAPIHeaderRepresentable, + Identifiable, DescriptionProperty, RequiredProperty, DeprecatedProperty, @@ -20,14 +21,20 @@ public protocol HeaderRepresentable: } public extension HeaderRepresentable { + + func reference() -> HeaderReference { + .init(self) + } - func openAPIHeader() -> OpenAPI.Header { + func openAPIHeader() -> Either, OpenAPI.Header> { .init( - schema: schema.openAPISchema(), - description: description, - required: required, - deprecated: deprecated, - vendorExtensions: vendorExtensions + .init( + schema: schema.openAPISchema(), + description: description, + required: required, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) ) } diff --git a/Sources/FeatherOpenAPI/Header/OpenAPIHeaderRepresentable.swift b/Sources/FeatherOpenAPI/Header/OpenAPIHeaderRepresentable.swift index 7e787d0..7fa567d 100644 --- a/Sources/FeatherOpenAPI/Header/OpenAPIHeaderRepresentable.swift +++ b/Sources/FeatherOpenAPI/Header/OpenAPIHeaderRepresentable.swift @@ -8,12 +8,12 @@ import OpenAPIKit30 public protocol OpenAPIHeaderRepresentable { - func openAPIHeader() -> OpenAPI.Header + func openAPIHeader() -> Either, OpenAPI.Header> } extension OpenAPI.Header: OpenAPIHeaderRepresentable { - public func openAPIHeader() -> OpenAPI.Header { - self + public func openAPIHeader() -> Either, OpenAPI.Header> { + .init(self) } } diff --git a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift index f1420a5..ff0b154 100644 --- a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift +++ b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift @@ -15,7 +15,10 @@ public protocol OperationRepresentable: DeprecatedProperty, VendorExtensionsProperty, // references - ReferencedSchemaMapRepresentable + ReferencedSchemaMapRepresentable, + ReferencedHeaderMapRepresentable, + ReferencedRequestBodyMapRepresentable, + ReferencedParameterMapRepresentable { // associatedtype RequestBodyType: RequestBodyRepresentable @@ -95,6 +98,23 @@ public extension OperationRepresentable { } return results } + + var referencedHeaderMap: OrderedDictionary { + var results = OrderedDictionary() + + let headers = responseMap.values + .map { $0.headerMap.values } + .flatMap { $0 } + + for header in headers { + if let ref = header as? HeaderReferenceRepresentable { + if case let .b(header) = ref.object.openAPIHeader() { + results[ref.id] = header + } + } + } + return results + } var referencedSchemaMap: OrderedDictionary { var results = OrderedDictionary() diff --git a/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift b/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift index c1a2c0a..d6ca2b0 100644 --- a/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift +++ b/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift @@ -10,7 +10,8 @@ import OpenAPIKit30 public protocol PathCollectionRepresentable: ReferencedSchemaMapRepresentable, ReferencedParameterMapRepresentable, - ReferencedRequestBodyMapRepresentable + ReferencedRequestBodyMapRepresentable, + ReferencedHeaderMapRepresentable { var pathMap: PathMap { get } var components: Components { get } @@ -57,11 +58,25 @@ extension PathCollectionRepresentable { return results } + var referencedHeaderMap: OrderedDictionary { + var results = OrderedDictionary() + + let headerMaps = pathMap.values + .map { $0.referencedHeaderMap } + .flatMap { $0 } + + for (k, v) in headerMaps { + results[k] = v + } + return results + } + var components: Components { .init( schemas: referencedSchemaMap.mapValues { $0.openAPISchema() }, parameters: referencedParameterMap.mapValues { $0.openAPIParameter().b }.compactMapValues { $0 }, - requestBodies: referencedRequestBodyMap.mapValues { $0.openAPIRequestBody().b }.compactMapValues { $0 } + requestBodies: referencedRequestBodyMap.mapValues { $0.openAPIRequestBody().b }.compactMapValues { $0 }, + headers: referencedHeaderMap.mapValues { $0.openAPIHeader().b }.compactMapValues { $0 } ) } } diff --git a/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift b/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift index 6af42ab..d481c9c 100644 --- a/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift +++ b/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift @@ -15,7 +15,8 @@ public protocol PathItemRepresentable: // reference ReferencedSchemaMapRepresentable, ReferencedParameterMapRepresentable, - ReferencedRequestBodyMapRepresentable + ReferencedRequestBodyMapRepresentable, + ReferencedHeaderMapRepresentable { var summary: String? { get } @@ -108,4 +109,15 @@ public extension PathItemRepresentable { } return results } + + var referencedHeaderMap: OrderedDictionary { + var results = OrderedDictionary() + + let maps = allOperations.map { $0.referencedHeaderMap }.flatMap { $0 } + + for (k, v) in maps { + results[k] = v + } + return results + } } diff --git a/Sources/FeatherOpenAPI/Reference/HeaderReferenceRepresentable.swift b/Sources/FeatherOpenAPI/Reference/HeaderReferenceRepresentable.swift new file mode 100644 index 0000000..7feaa3c --- /dev/null +++ b/Sources/FeatherOpenAPI/Reference/HeaderReferenceRepresentable.swift @@ -0,0 +1,38 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol HeaderReferenceRepresentable { + var id: HeaderID { get } + var object: HeaderRepresentable { get } +} + +public struct HeaderReference: + HeaderRepresentable, + HeaderReferenceRepresentable +{ + public var schema: OpenAPISchemaRepresentable { object.schema } + + public var object: HeaderRepresentable { + _object + } + + public var id: HeaderID + public var _object: T + + public init( + _ object: T + ) { + self.id = .init(object.openAPIIdentifier) + self._object = object + } + + public func openAPIHeader() -> Either, OpenAPI.Header> { + .reference(.component(named: id.rawValue)) + } +} diff --git a/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift b/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift index 90ed963..2bf2a68 100644 --- a/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift +++ b/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift @@ -19,3 +19,7 @@ public protocol ReferencedParameterMapRepresentable { public protocol ReferencedRequestBodyMapRepresentable { var referencedRequestBodyMap: OrderedDictionary { get } } + +public protocol ReferencedHeaderMapRepresentable { + var referencedHeaderMap: OrderedDictionary { get } +} diff --git a/Sources/FeatherOpenAPI/Response/Abstraction/ResponseRepresentable.swift b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseRepresentable.swift index 16ec2c0..6269332 100644 --- a/Sources/FeatherOpenAPI/Response/Abstraction/ResponseRepresentable.swift +++ b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseRepresentable.swift @@ -23,7 +23,7 @@ public extension ResponseRepresentable { func openAPIResponse() -> OpenAPI.Response { .init( description: description, - headers: headerMap.mapValues { .init($0.openAPIHeader()) }, + headers: headerMap.mapValues { $0.openAPIHeader() }, content: contentMap.mapValues { $0.openAPIContent() }, links: [:], vendorExtensions: vendorExtensions diff --git a/Tests/FeatherOpenAPITests/Example/TestObjects.swift b/Tests/FeatherOpenAPITests/Example/TestObjects.swift index d0b388b..f5eefe0 100644 --- a/Tests/FeatherOpenAPITests/Example/TestObjects.swift +++ b/Tests/FeatherOpenAPITests/Example/TestObjects.swift @@ -79,6 +79,12 @@ struct TodoCreateRequestBody: RequestBodyRepresentable { struct TodoCreateResponse: JSONResponseRepresentable { var description: String = "Todo response" var schema = TodoDetailObject() + + var headerMap: HeaderMap { + [ + "x-custom-header": CustomHeader().reference() + ] + } } struct TodoIdParameter: ParameterRepresentable { @@ -88,6 +94,10 @@ struct TodoIdParameter: ParameterRepresentable { var schema: any FeatherOpenAPI.OpenAPISchemaRepresentable { TodoIDField().reference() } } +struct CustomHeader: HeaderRepresentable { + var schema: any OpenAPISchemaRepresentable { TodoIDField().reference() } +} + struct TodoCreateOperation: OperationRepresentable { @@ -97,6 +107,7 @@ struct TodoCreateOperation: OperationRepresentable { TodoIdParameter().reference(), ] } + var requestBody: RequestBodyRepresentable? = TodoCreateRequestBody().reference() var responseMap: ResponseMap { From fc1e933b7e4e9f61e01f4727f6c4cdd57d74af65 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sat, 24 Jan 2026 08:44:03 +0100 Subject: [PATCH 19/34] response references --- .../Components/ComponentsRepresentable.swift | 4 +- .../Operation/OperationRepresentable.swift | 20 ++++++- .../PathCollectionRepresentable.swift | 29 +++++++--- .../PathItem/PathItemRepresentable.swift | 14 ++++- .../ReferencedSchemaMapRepresentable.swift | 4 ++ .../ResponseReferenceRepresentable.swift | 40 +++++++++++++ .../OpenAPIResponseRepresentable.swift | 8 +-- .../Abstraction/ResponseRepresentable.swift | 19 ++++-- .../Example/TestObjects.swift | 58 +++++-------------- .../FeatherOpenAPIKitTests.swift | 1 + 10 files changed, 133 insertions(+), 64 deletions(-) create mode 100644 Sources/FeatherOpenAPI/Reference/ResponseReferenceRepresentable.swift diff --git a/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift b/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift index 3d8eed0..ac70980 100644 --- a/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift +++ b/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift @@ -77,7 +77,9 @@ public extension ComponentsRepresentable { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in responses { - result[.init(stringLiteral: key.rawValue)] = value.openAPIResponse() + if let response = value.openAPIResponse().b { + result[.init(stringLiteral: key.rawValue)] = response + } } return result } diff --git a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift index ff0b154..daee5e3 100644 --- a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift +++ b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift @@ -18,7 +18,8 @@ public protocol OperationRepresentable: ReferencedSchemaMapRepresentable, ReferencedHeaderMapRepresentable, ReferencedRequestBodyMapRepresentable, - ReferencedParameterMapRepresentable + ReferencedParameterMapRepresentable, + ReferencedResponseMapRepresentable { // associatedtype RequestBodyType: RequestBodyRepresentable @@ -53,7 +54,7 @@ public extension OperationRepresentable { operationId: operationId, parameters: parameters.map { $0.openAPIParameter() }, requestBody: requestBody.openAPIRequestBody(), - responses: responseMap.mapValues { .init($0.openAPIResponse()) }, + responses: responseMap.mapValues { $0.openAPIResponse() }, callbacks: [:], deprecated: deprecated, security: nil, @@ -68,7 +69,7 @@ public extension OperationRepresentable { externalDocs: nil, operationId: operationId, parameters: parameters.map { $0.openAPIParameter() }, - responses: responseMap.mapValues { .init($0.openAPIResponse()) }, + responses: responseMap.mapValues { $0.openAPIResponse() }, callbacks: [:], deprecated: deprecated, security: nil, @@ -115,6 +116,19 @@ public extension OperationRepresentable { } return results } + + var referencedResponseMap: OrderedDictionary { + var results = OrderedDictionary() + + for response in responseMap.values { + if let ref = response as? ResponseReferenceRepresentable { + if case let .b(response) = ref.object.openAPIResponse() { + results[ref.id] = response + } + } + } + return results + } var referencedSchemaMap: OrderedDictionary { var results = OrderedDictionary() diff --git a/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift b/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift index d6ca2b0..bf39f3d 100644 --- a/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift +++ b/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift @@ -11,10 +11,11 @@ public protocol PathCollectionRepresentable: ReferencedSchemaMapRepresentable, ReferencedParameterMapRepresentable, ReferencedRequestBodyMapRepresentable, - ReferencedHeaderMapRepresentable + ReferencedHeaderMapRepresentable, + ReferencedResponseMapRepresentable { var pathMap: PathMap { get } - var components: Components { get } + var components: FeatherOpenAPI.Components { get } } extension PathCollectionRepresentable { @@ -71,12 +72,26 @@ extension PathCollectionRepresentable { return results } - var components: Components { + var referencedResponseMap: OrderedDictionary { + var results = OrderedDictionary() + + let responseMaps = pathMap.values + .map { $0.referencedResponseMap } + .flatMap { $0 } + + for (k, v) in responseMaps { + results[k] = v + } + return results + } + + var components: FeatherOpenAPI.Components { .init( - schemas: referencedSchemaMap.mapValues { $0.openAPISchema() }, - parameters: referencedParameterMap.mapValues { $0.openAPIParameter().b }.compactMapValues { $0 }, - requestBodies: referencedRequestBodyMap.mapValues { $0.openAPIRequestBody().b }.compactMapValues { $0 }, - headers: referencedHeaderMap.mapValues { $0.openAPIHeader().b }.compactMapValues { $0 } + schemas: referencedSchemaMap, + parameters: referencedParameterMap, + responses: referencedResponseMap, + requestBodies: referencedRequestBodyMap, + headers: referencedHeaderMap, ) } } diff --git a/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift b/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift index d481c9c..c22150e 100644 --- a/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift +++ b/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift @@ -16,7 +16,8 @@ public protocol PathItemRepresentable: ReferencedSchemaMapRepresentable, ReferencedParameterMapRepresentable, ReferencedRequestBodyMapRepresentable, - ReferencedHeaderMapRepresentable + ReferencedHeaderMapRepresentable, + ReferencedResponseMapRepresentable { var summary: String? { get } @@ -120,4 +121,15 @@ public extension PathItemRepresentable { } return results } + + var referencedResponseMap: OrderedDictionary { + var results = OrderedDictionary() + + let maps = allOperations.map { $0.referencedResponseMap }.flatMap { $0 } + + for (k, v) in maps { + results[k] = v + } + return results + } } diff --git a/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift b/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift index 2bf2a68..d9729bb 100644 --- a/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift +++ b/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift @@ -23,3 +23,7 @@ public protocol ReferencedRequestBodyMapRepresentable { public protocol ReferencedHeaderMapRepresentable { var referencedHeaderMap: OrderedDictionary { get } } + +public protocol ReferencedResponseMapRepresentable { + var referencedResponseMap: OrderedDictionary { get } +} diff --git a/Sources/FeatherOpenAPI/Reference/ResponseReferenceRepresentable.swift b/Sources/FeatherOpenAPI/Reference/ResponseReferenceRepresentable.swift new file mode 100644 index 0000000..a6c362c --- /dev/null +++ b/Sources/FeatherOpenAPI/Reference/ResponseReferenceRepresentable.swift @@ -0,0 +1,40 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 23.. +// + +import OpenAPIKit30 + +public protocol ResponseReferenceRepresentable { + var id: ResponseID { get } + var object: ResponseRepresentable { get } +} + +public struct ResponseReference: + ResponseRepresentable, + ResponseReferenceRepresentable +{ + public var description: String { object.description } + public var headerMap: HeaderMap { object.headerMap } + public var contentMap: ContentMap { object.contentMap } + + public var object: ResponseRepresentable { + _object + } + + public var id: ResponseID + public var _object: T + + internal init( + _ object: T + ) { + self.id = .init(object.openAPIIdentifier) + self._object = object + } + + public func openAPIResponse() -> Either, OpenAPI.Response> { + .reference(.component(named: id.rawValue)) + } +} diff --git a/Sources/FeatherOpenAPI/Response/Abstraction/OpenAPIResponseRepresentable.swift b/Sources/FeatherOpenAPI/Response/Abstraction/OpenAPIResponseRepresentable.swift index 0c81a50..824a9a2 100644 --- a/Sources/FeatherOpenAPI/Response/Abstraction/OpenAPIResponseRepresentable.swift +++ b/Sources/FeatherOpenAPI/Response/Abstraction/OpenAPIResponseRepresentable.swift @@ -8,12 +8,12 @@ import OpenAPIKit30 public protocol OpenAPIResponseRepresentable { - func openAPIResponse() -> OpenAPI.Response + func openAPIResponse() -> Either, OpenAPI.Response> } extension OpenAPI.Response: OpenAPIResponseRepresentable { - - public func openAPIResponse() -> OpenAPI.Response { - self + + public func openAPIResponse() -> Either, OpenAPI.Response> { + .init(self) } } diff --git a/Sources/FeatherOpenAPI/Response/Abstraction/ResponseRepresentable.swift b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseRepresentable.swift index 6269332..d89c094 100644 --- a/Sources/FeatherOpenAPI/Response/Abstraction/ResponseRepresentable.swift +++ b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseRepresentable.swift @@ -9,6 +9,7 @@ import OpenAPIKit30 public protocol ResponseRepresentable: OpenAPIResponseRepresentable, + Identifiable, VendorExtensionsProperty { var description: String { get } @@ -19,14 +20,20 @@ public protocol ResponseRepresentable: public extension ResponseRepresentable { var headerMap: HeaderMap { [:] } + + func reference() -> ResponseReference { + .init(self) + } - func openAPIResponse() -> OpenAPI.Response { + func openAPIResponse() -> Either, OpenAPI.Response> { .init( - description: description, - headers: headerMap.mapValues { $0.openAPIHeader() }, - content: contentMap.mapValues { $0.openAPIContent() }, - links: [:], - vendorExtensions: vendorExtensions + .init( + description: description, + headers: headerMap.mapValues { $0.openAPIHeader() }, + content: contentMap.mapValues { $0.openAPIContent() }, + links: [:], + vendorExtensions: vendorExtensions + ) ) } } diff --git a/Tests/FeatherOpenAPITests/Example/TestObjects.swift b/Tests/FeatherOpenAPITests/Example/TestObjects.swift index f5eefe0..4b09e9f 100644 --- a/Tests/FeatherOpenAPITests/Example/TestObjects.swift +++ b/Tests/FeatherOpenAPITests/Example/TestObjects.swift @@ -9,36 +9,12 @@ import FeatherOpenAPI import OpenAPIKit30 -struct ExampleFieldId: StringSchemaRepresentable { - - var title: String? { "foo" } -} - -public struct ExampleFieldName: StringSchemaRepresentable { - - public var example: String? - - public init( - example: String? = "John Doe" - ) { - self.example = example - } -} - struct TodoIDField: IntSchemaRepresentable { var example: Int? { 1 } -// var openAPIIdentifier: String { "foo_\(example)" } -// var openAPIIdentifierSuffix: String { String(example) } - -// init(example: Int? = 1) { -// self.example = example -// } } struct TodoTitleField: StringSchemaRepresentable { var example: String? = "Buy milk" - - } struct TodoIsCompleteField: BoolSchemaRepresentable { @@ -49,18 +25,9 @@ struct TodoDetailObject: ObjectSchemaRepresentable { var propertyMap: SchemaMap { [ -// "id1": TodoIDField(example: 1).referenced(id: "foo"), -// "id2": TodoIDField(example: 2).referenced(id: "bar"), -// "id3": TodoIDField() -// .inlined(example: 2) -// .inlined(example: 123) -// .inlined(), -> Self -// .referenced() -> .component(named: "") + "id": TodoIDField().reference(), "title": TodoTitleField(), "isComplete": TodoIsCompleteField(), - // TODO: move required to schema -> use that to reference -// "bar": SchemaReference(TodoIDField(), required: false), -// "foo": TodoIDField(example: 12).referenced(id: ""), // "unsafe": UnsafeSchemaReference("asdf"), ] } @@ -78,7 +45,7 @@ struct TodoCreateRequestBody: RequestBodyRepresentable { struct TodoCreateResponse: JSONResponseRepresentable { var description: String = "Todo response" - var schema = TodoDetailObject() + var schema = TodoDetailObject().reference() var headerMap: HeaderMap { [ @@ -90,12 +57,18 @@ struct TodoCreateResponse: JSONResponseRepresentable { struct TodoIdParameter: ParameterRepresentable { var name: String { "todoId" } - var context: OpenAPIKit30.OpenAPI.Parameter.Context { .path } - var schema: any FeatherOpenAPI.OpenAPISchemaRepresentable { TodoIDField().reference() } + var context: OpenAPIKit30.OpenAPI.Parameter.Context { + .path + } + var schema: any OpenAPISchemaRepresentable { + TodoIDField().reference() + } } struct CustomHeader: HeaderRepresentable { - var schema: any OpenAPISchemaRepresentable { TodoIDField().reference() } + var schema: any OpenAPISchemaRepresentable { + TodoIDField().reference() + } } @@ -103,16 +76,17 @@ struct TodoCreateOperation: OperationRepresentable { var parameters: [ParameterRepresentable] { [ -// TodoIdParameter(), TodoIdParameter().reference(), ] } - - var requestBody: RequestBodyRepresentable? = TodoCreateRequestBody().reference() + var requestBody: RequestBodyRepresentable? { + TodoCreateRequestBody().reference() + } + var responseMap: ResponseMap { [ - 200: TodoCreateResponse(), + 200: TodoCreateResponse().reference(), ] } } diff --git a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift index c93ac4e..4c6d545 100644 --- a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift +++ b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift @@ -44,6 +44,7 @@ struct FeatherOpenAPIKitTests { struct MyPathCollection: PathCollectionRepresentable { + var pathMap: PathMap { [ "todos": TodoPathItems(), From eefbf1877ddb20b8207581f4bbb1d8746c82e551 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sat, 24 Jan 2026 08:59:10 +0100 Subject: [PATCH 20/34] tags --- .../Document/DocumentRepresentable.swift | 9 +++++++-- .../Operation/OperationRepresentable.swift | 14 ++++++++++---- .../PathCollectionRepresentable.swift | 7 ++++++- .../PathItem/PathItemRepresentable.swift | 7 ++++++- .../ReferencedSchemaMapRepresentable.swift | 5 ++++- Sources/FeatherOpenAPI/Tag/TagRepresentable.swift | 6 +++--- .../FeatherOpenAPITests/Example/TestObjects.swift | 10 ++++++++++ .../FeatherOpenAPIKitTests.swift | 2 +- 8 files changed, 47 insertions(+), 13 deletions(-) diff --git a/Sources/FeatherOpenAPI/Document/DocumentRepresentable.swift b/Sources/FeatherOpenAPI/Document/DocumentRepresentable.swift index ee09631..72cc2b0 100644 --- a/Sources/FeatherOpenAPI/Document/DocumentRepresentable.swift +++ b/Sources/FeatherOpenAPI/Document/DocumentRepresentable.swift @@ -9,7 +9,8 @@ import OpenAPIKit30 public protocol DocumentRepresentable: OpenAPIDocumentRepresentable, - VendorExtensionsProperty + VendorExtensionsProperty, + ReferencedTagMapRepresentable { var info: OpenAPIInfoRepresentable { get } var servers: [OpenAPIServerRepresentable] { get } @@ -26,6 +27,10 @@ public extension DocumentRepresentable { var security: [OpenAPISecurityRequirementRepresentable] { [] } var externalDocs: ExternalDocsRepresentable? { nil } + + var referencedTags: [OpenAPITagRepresentable] { + paths.values.map { $0.referencedTags }.flatMap { $0 } + } func openAPIDocument() -> OpenAPI.Document { .init( @@ -35,7 +40,7 @@ public extension DocumentRepresentable { paths: paths.mapValues { .init($0.openAPIPathItem()) }, components: components.openAPIComponents(), security: security.map { $0.openAPISecurityRequirement() }, - tags: nil, + tags: referencedTags.map { $0.openAPITag() }, externalDocs: externalDocs?.openAPIExternalDocs(), vendorExtensions: vendorExtensions ) diff --git a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift index daee5e3..49f5c0f 100644 --- a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift +++ b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift @@ -19,11 +19,12 @@ public protocol OperationRepresentable: ReferencedHeaderMapRepresentable, ReferencedRequestBodyMapRepresentable, ReferencedParameterMapRepresentable, - ReferencedResponseMapRepresentable + ReferencedResponseMapRepresentable, + ReferencedTagMapRepresentable { // associatedtype RequestBodyType: RequestBodyRepresentable -// var tags: [String]? + var tags: [TagRepresentable] { get } var summary: String? { get } var operationId: String? { get } @@ -37,6 +38,7 @@ public protocol OperationRepresentable: public extension OperationRepresentable { + var tags: [TagRepresentable] { [] } var summary: String? { nil } var operationId: String? { nil } @@ -47,7 +49,7 @@ public extension OperationRepresentable { func openAPIOperation() -> OpenAPI.Operation { if let requestBody { return .init( - tags: nil, + tags: tags.isEmpty ? nil : tags.map { $0.name }, summary: summary, description: description, externalDocs: nil, @@ -63,7 +65,7 @@ public extension OperationRepresentable { ) } return .init( - tags: nil, + tags: tags.isEmpty ? nil : tags.map { $0.name }, summary: summary, description: description, externalDocs: nil, @@ -129,6 +131,10 @@ public extension OperationRepresentable { } return results } + + var referencedTags: [OpenAPITagRepresentable] { + tags + } var referencedSchemaMap: OrderedDictionary { var results = OrderedDictionary() diff --git a/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift b/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift index bf39f3d..ad7e774 100644 --- a/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift +++ b/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift @@ -12,7 +12,8 @@ public protocol PathCollectionRepresentable: ReferencedParameterMapRepresentable, ReferencedRequestBodyMapRepresentable, ReferencedHeaderMapRepresentable, - ReferencedResponseMapRepresentable + ReferencedResponseMapRepresentable, + ReferencedTagMapRepresentable { var pathMap: PathMap { get } var components: FeatherOpenAPI.Components { get } @@ -85,6 +86,10 @@ extension PathCollectionRepresentable { return results } + var referencedTags: [OpenAPITagRepresentable] { + pathMap.values.map { $0.referencedTags }.flatMap { $0 } + } + var components: FeatherOpenAPI.Components { .init( schemas: referencedSchemaMap, diff --git a/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift b/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift index c22150e..e81c11a 100644 --- a/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift +++ b/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift @@ -17,7 +17,8 @@ public protocol PathItemRepresentable: ReferencedParameterMapRepresentable, ReferencedRequestBodyMapRepresentable, ReferencedHeaderMapRepresentable, - ReferencedResponseMapRepresentable + ReferencedResponseMapRepresentable, + ReferencedTagMapRepresentable { var summary: String? { get } @@ -132,4 +133,8 @@ public extension PathItemRepresentable { } return results } + + var referencedTags: [OpenAPITagRepresentable] { + allOperations.map { $0.referencedTags }.flatMap { $0 } + } } diff --git a/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift b/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift index d9729bb..2203ff8 100644 --- a/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift +++ b/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift @@ -11,7 +11,6 @@ public protocol ReferencedSchemaMapRepresentable { var referencedSchemaMap: OrderedDictionary { get } } - public protocol ReferencedParameterMapRepresentable { var referencedParameterMap: OrderedDictionary { get } } @@ -27,3 +26,7 @@ public protocol ReferencedHeaderMapRepresentable { public protocol ReferencedResponseMapRepresentable { var referencedResponseMap: OrderedDictionary { get } } + +public protocol ReferencedTagMapRepresentable { + var referencedTags: [OpenAPITagRepresentable] { get } +} diff --git a/Sources/FeatherOpenAPI/Tag/TagRepresentable.swift b/Sources/FeatherOpenAPI/Tag/TagRepresentable.swift index 2f91dfa..719cb27 100644 --- a/Sources/FeatherOpenAPI/Tag/TagRepresentable.swift +++ b/Sources/FeatherOpenAPI/Tag/TagRepresentable.swift @@ -9,6 +9,7 @@ import OpenAPIKit30 public protocol TagRepresentable: OpenAPITagRepresentable, + Identifiable, DescriptionProperty, VendorExtensionsProperty { @@ -16,11 +17,11 @@ public protocol TagRepresentable: var externalDocs: ExternalDocsRepresentable? { get } } -extension TagRepresentable { +public extension TagRepresentable { var externalDocs: ExternalDocsRepresentable? { nil } - public func openAPITag() -> OpenAPI.Tag { + func openAPITag() -> OpenAPI.Tag { .init( name: name, description: description, @@ -30,4 +31,3 @@ extension TagRepresentable { } } - diff --git a/Tests/FeatherOpenAPITests/Example/TestObjects.swift b/Tests/FeatherOpenAPITests/Example/TestObjects.swift index 4b09e9f..52819b1 100644 --- a/Tests/FeatherOpenAPITests/Example/TestObjects.swift +++ b/Tests/FeatherOpenAPITests/Example/TestObjects.swift @@ -71,9 +71,19 @@ struct CustomHeader: HeaderRepresentable { } } +struct TodoTag: TagRepresentable { + var name: String = "Todos" + var description: String? = "This is the todo tag." +} struct TodoCreateOperation: OperationRepresentable { + var tags: [TagRepresentable] { + [ + TodoTag() + ] + } + var parameters: [ParameterRepresentable] { [ TodoIdParameter().reference(), diff --git a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift index 4c6d545..050cf8d 100644 --- a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift +++ b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift @@ -59,7 +59,7 @@ struct FeatherOpenAPIKitTests { let document = MyDocument( info: MyInfo(), paths: collection.pathMap, - components: collection.components + components: collection.components, ) let openAPIdoc = document.openAPIDocument() From b90ac8e3d2a5c5e630c3d450a71343e1e0fbb400 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sat, 24 Jan 2026 10:12:43 +0100 Subject: [PATCH 21/34] security --- .../Components/Components.swift | 6 +- .../Components/ComponentsRepresentable.swift | 20 ++--- .../Document/DocumentRepresentable.swift | 11 ++- .../Operation/OperationRepresentable.swift | 90 +++++++++++-------- .../PathCollectionRepresentable.swift | 8 +- .../PathItem/PathItemRepresentable.swift | 7 +- .../ReferencedSchemaMapRepresentable.swift | 4 + .../Abstraction/RequestBodyReference.swift | 26 ------ .../BinaryRequestBodyRepresentable.swift | 14 --- .../FeatherOpenAPI/Schema/BinarySchema.swift | 21 +++++ .../SecurityRequirementRepresentable.swift | 18 ++-- .../SecurityScheme/SecuritySchemeID.swift | 17 ---- .../SecuritySchemeRepresentable.swift | 1 + .../Example/TestObjects.swift | 33 +++++++ 14 files changed, 152 insertions(+), 124 deletions(-) delete mode 100644 Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyReference.swift create mode 100644 Sources/FeatherOpenAPI/Schema/BinarySchema.swift delete mode 100644 Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeID.swift diff --git a/Sources/FeatherOpenAPI/Components/Components.swift b/Sources/FeatherOpenAPI/Components/Components.swift index 82345d2..5faf87c 100644 --- a/Sources/FeatherOpenAPI/Components/Components.swift +++ b/Sources/FeatherOpenAPI/Components/Components.swift @@ -15,7 +15,7 @@ public struct Components: ComponentsRepresentable { public var responses: OrderedDictionary public var requestBodies: OrderedDictionary public var headers: OrderedDictionary - public var securitySchemes: OrderedDictionary + public var securityRequirements: [SecurityRequirementRepresentable] public var links: OrderedDictionary public init( @@ -25,7 +25,7 @@ public struct Components: ComponentsRepresentable { responses: OrderedDictionary = [:], requestBodies: OrderedDictionary = [:], headers: OrderedDictionary = [:], - securitySchemes: OrderedDictionary = [:], + securityRequirements: [SecurityRequirementRepresentable] = [], links: OrderedDictionary = [:], ) { self.schemas = schemas @@ -34,7 +34,7 @@ public struct Components: ComponentsRepresentable { self.responses = responses self.requestBodies = requestBodies self.headers = headers - self.securitySchemes = securitySchemes + self.securityRequirements = securityRequirements self.links = links } } diff --git a/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift b/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift index ac70980..75c0159 100644 --- a/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift +++ b/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift @@ -11,14 +11,13 @@ public protocol ComponentsRepresentable: OpenAPIComponentsRepresentable, VendorExtensionsProperty { - var schemas: OrderedDictionary { get } var parameters: OrderedDictionary { get } var examples: OrderedDictionary { get } var responses: OrderedDictionary { get } var requestBodies: OrderedDictionary { get } var headers: OrderedDictionary { get } - var securitySchemes: OrderedDictionary { get } + var securityRequirements: [SecurityRequirementRepresentable] { get } var links: OrderedDictionary { get } // public var callbacks: OrderedDictionary @@ -33,16 +32,6 @@ public protocol ComponentsRepresentable: } public extension ComponentsRepresentable { - - var schemas: OrderedDictionary { [:] } - var parameters: OrderedDictionary { [:] } - var examples: OrderedDictionary { [:] } - var responses: OrderedDictionary { [:] } - var requestBodies: OrderedDictionary { [:] } - var headers: OrderedDictionary { [:] } - var securitySchemes: OrderedDictionary { [:] } - var links: OrderedDictionary { [:] } - func openAPISchemas() -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] @@ -112,9 +101,10 @@ public extension ComponentsRepresentable { { var result: OpenAPI.ComponentDictionary = [:] - for (key, value) in securitySchemes { - result[.init(stringLiteral: key.rawValue)] = - value.openAPISecurityScheme() + print(securityRequirements) + for requirement in securityRequirements { + let scheme = requirement.security + result[.init(stringLiteral: scheme.openAPIIdentifier)] = scheme.openAPISecurityScheme() } return result } diff --git a/Sources/FeatherOpenAPI/Document/DocumentRepresentable.swift b/Sources/FeatherOpenAPI/Document/DocumentRepresentable.swift index 72cc2b0..a1919c8 100644 --- a/Sources/FeatherOpenAPI/Document/DocumentRepresentable.swift +++ b/Sources/FeatherOpenAPI/Document/DocumentRepresentable.swift @@ -10,13 +10,13 @@ import OpenAPIKit30 public protocol DocumentRepresentable: OpenAPIDocumentRepresentable, VendorExtensionsProperty, - ReferencedTagMapRepresentable + ReferencedTagMapRepresentable, + ReferencedSecuritySchemeMapRepresentable { var info: OpenAPIInfoRepresentable { get } var servers: [OpenAPIServerRepresentable] { get } var paths: PathMap { get } var components: OpenAPIComponentsRepresentable { get } - var security: [OpenAPISecurityRequirementRepresentable] { get } var externalDocs: ExternalDocsRepresentable? { get } } @@ -25,12 +25,15 @@ public extension DocumentRepresentable { var servers: [OpenAPIServerRepresentable] { [] } var paths: PathMap { [:] } - var security: [OpenAPISecurityRequirementRepresentable] { [] } var externalDocs: ExternalDocsRepresentable? { nil } var referencedTags: [OpenAPITagRepresentable] { paths.values.map { $0.referencedTags }.flatMap { $0 } } + + var referencedSecurityRequirements: [SecurityRequirementRepresentable] { + paths.values.map { $0.referencedSecurityRequirements }.flatMap { $0 } + } func openAPIDocument() -> OpenAPI.Document { .init( @@ -39,7 +42,7 @@ public extension DocumentRepresentable { servers: servers.map { $0.openAPIServer() }, paths: paths.mapValues { .init($0.openAPIPathItem()) }, components: components.openAPIComponents(), - security: security.map { $0.openAPISecurityRequirement() }, + security: referencedSecurityRequirements.map { $0.openAPISecurityRequirement() }, tags: referencedTags.map { $0.openAPITag() }, externalDocs: externalDocs?.openAPIExternalDocs(), vendorExtensions: vendorExtensions diff --git a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift index 49f5c0f..f2e8eb0 100644 --- a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift +++ b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift @@ -20,7 +20,8 @@ public protocol OperationRepresentable: ReferencedRequestBodyMapRepresentable, ReferencedParameterMapRepresentable, ReferencedResponseMapRepresentable, - ReferencedTagMapRepresentable + ReferencedTagMapRepresentable, + ReferencedSecuritySchemeMapRepresentable { // associatedtype RequestBodyType: RequestBodyRepresentable @@ -32,7 +33,7 @@ public protocol OperationRepresentable: var requestBody: RequestBodyRepresentable? { get } var responseMap: ResponseMap { get } -// var security: [SecurityRequirementRepresentable]? + var security: [SecurityRequirementRepresentable]? { get } // var servers: [ServerRepresentable]? } @@ -45,11 +46,24 @@ public extension OperationRepresentable { var parameters: [ParameterRepresentable] { [] } var requestBody: RequestBodyRepresentable? { nil } + var security: [SecurityRequirementRepresentable]? { nil } + + private var openAPITags: [String]? { + tags.isEmpty ? nil : tags.map { $0.name } + } + + private var openAPISecurityRequirements: [OpenAPI.SecurityRequirement]? { + guard let security, !security.isEmpty else { + return nil + } + + return security.map { $0.openAPISecurityRequirement() } + } func openAPIOperation() -> OpenAPI.Operation { if let requestBody { return .init( - tags: tags.isEmpty ? nil : tags.map { $0.name }, + tags: openAPITags, summary: summary, description: description, externalDocs: nil, @@ -59,13 +73,13 @@ public extension OperationRepresentable { responses: responseMap.mapValues { $0.openAPIResponse() }, callbacks: [:], deprecated: deprecated, - security: nil, + security: openAPISecurityRequirements, servers: nil, vendorExtensions: vendorExtensions ) } return .init( - tags: tags.isEmpty ? nil : tags.map { $0.name }, + tags: openAPITags, summary: summary, description: description, externalDocs: nil, @@ -74,12 +88,44 @@ public extension OperationRepresentable { responses: responseMap.mapValues { $0.openAPIResponse() }, callbacks: [:], deprecated: deprecated, - security: nil, + security: openAPISecurityRequirements, servers: nil, vendorExtensions: vendorExtensions ) } + // MARK: - refs + + var referencedSchemaMap: OrderedDictionary { + var results = OrderedDictionary() + + for parameter in parameters { + results.merge(parameter.referencedSchemaMap) + } + + if let schemaMap = requestBody?.referencedSchemaMap { + results.merge(schemaMap) + } + + let headers = responseMap.values + .map { $0.headerMap.values } + .flatMap { $0 } + + for header in headers { + results.merge(header.referencedSchemaMap) + } + + let contents = responseMap.values + .map { $0.contentMap.values } + .flatMap { $0 } + + for content in contents { + results.merge(content.referencedSchemaMap) + } + + return results + } + var referencedParameterMap: OrderedDictionary { var results = OrderedDictionary() @@ -135,34 +181,8 @@ public extension OperationRepresentable { var referencedTags: [OpenAPITagRepresentable] { tags } - - var referencedSchemaMap: OrderedDictionary { - var results = OrderedDictionary() - - for parameter in parameters { - results.merge(parameter.referencedSchemaMap) - } - - if let schemaMap = requestBody?.referencedSchemaMap { - results.merge(schemaMap) - } - - let headers = responseMap.values - .map { $0.headerMap.values } - .flatMap { $0 } - - for header in headers { - results.merge(header.referencedSchemaMap) - } - - let contents = responseMap.values - .map { $0.contentMap.values } - .flatMap { $0 } - - for content in contents { - results.merge(content.referencedSchemaMap) - } - - return results + + var referencedSecurityRequirements: [SecurityRequirementRepresentable] { + security?.map { $0 } ?? [] } } diff --git a/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift b/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift index ad7e774..6b8c695 100644 --- a/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift +++ b/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift @@ -13,7 +13,8 @@ public protocol PathCollectionRepresentable: ReferencedRequestBodyMapRepresentable, ReferencedHeaderMapRepresentable, ReferencedResponseMapRepresentable, - ReferencedTagMapRepresentable + ReferencedTagMapRepresentable, + ReferencedSecuritySchemeMapRepresentable { var pathMap: PathMap { get } var components: FeatherOpenAPI.Components { get } @@ -89,6 +90,10 @@ extension PathCollectionRepresentable { var referencedTags: [OpenAPITagRepresentable] { pathMap.values.map { $0.referencedTags }.flatMap { $0 } } + + public var referencedSecurityRequirements: [SecurityRequirementRepresentable] { + pathMap.values.map { $0.referencedSecurityRequirements }.flatMap { $0 } + } var components: FeatherOpenAPI.Components { .init( @@ -97,6 +102,7 @@ extension PathCollectionRepresentable { responses: referencedResponseMap, requestBodies: referencedRequestBodyMap, headers: referencedHeaderMap, + securityRequirements: referencedSecurityRequirements ) } } diff --git a/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift b/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift index e81c11a..4d11fc8 100644 --- a/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift +++ b/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift @@ -18,7 +18,8 @@ public protocol PathItemRepresentable: ReferencedRequestBodyMapRepresentable, ReferencedHeaderMapRepresentable, ReferencedResponseMapRepresentable, - ReferencedTagMapRepresentable + ReferencedTagMapRepresentable, + ReferencedSecuritySchemeMapRepresentable { var summary: String? { get } @@ -137,4 +138,8 @@ public extension PathItemRepresentable { var referencedTags: [OpenAPITagRepresentable] { allOperations.map { $0.referencedTags }.flatMap { $0 } } + + var referencedSecurityRequirements: [SecurityRequirementRepresentable] { + allOperations.map { $0.referencedSecurityRequirements }.flatMap { $0 } + } } diff --git a/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift b/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift index 2203ff8..69fa53e 100644 --- a/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift +++ b/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift @@ -27,6 +27,10 @@ public protocol ReferencedResponseMapRepresentable { var referencedResponseMap: OrderedDictionary { get } } +public protocol ReferencedSecuritySchemeMapRepresentable { + var referencedSecurityRequirements: [SecurityRequirementRepresentable] { get } +} + public protocol ReferencedTagMapRepresentable { var referencedTags: [OpenAPITagRepresentable] { get } } diff --git a/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyReference.swift b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyReference.swift deleted file mode 100644 index f0ebac3..0000000 --- a/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyReference.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 22.. -// - -import OpenAPIKit30 - -//public struct RequestBodyReference: RequestBodyRepresentable { -// -// public var id: String -// public var required: Bool -// -// public init( -// _ type: T, -// required: Bool = true -// ) { -// self.id = type.openAPIIdentifier -// self.required = required -// } -// -// public func openAPIRequestBody() -> OpenAPI.Request { -// . -// } -//} diff --git a/Sources/FeatherOpenAPI/RequestBody/BinaryRequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/BinaryRequestBodyRepresentable.swift index 4665bf8..30444d7 100644 --- a/Sources/FeatherOpenAPI/RequestBody/BinaryRequestBodyRepresentable.swift +++ b/Sources/FeatherOpenAPI/RequestBody/BinaryRequestBodyRepresentable.swift @@ -19,17 +19,3 @@ public extension BinaryRequestBodyRepresentable { ] } } - -#warning("move this") -struct BinarySchema: SchemaRepresentable { - - func openAPISchema() -> JSONSchema { - JSONSchema.string( - format: .binary - ) - } - - var referencedSchemaMap: OrderedDictionary { - [:] - } -} diff --git a/Sources/FeatherOpenAPI/Schema/BinarySchema.swift b/Sources/FeatherOpenAPI/Schema/BinarySchema.swift new file mode 100644 index 0000000..a1cec74 --- /dev/null +++ b/Sources/FeatherOpenAPI/Schema/BinarySchema.swift @@ -0,0 +1,21 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 24.. +// + +import OpenAPIKit30 + +struct BinarySchema: SchemaRepresentable { + + func openAPISchema() -> JSONSchema { + JSONSchema.string( + format: .binary + ) + } + + var referencedSchemaMap: OrderedDictionary { + [:] + } +} diff --git a/Sources/FeatherOpenAPI/SecurityRequirement/SecurityRequirementRepresentable.swift b/Sources/FeatherOpenAPI/SecurityRequirement/SecurityRequirementRepresentable.swift index a4b408d..31379ca 100644 --- a/Sources/FeatherOpenAPI/SecurityRequirement/SecurityRequirementRepresentable.swift +++ b/Sources/FeatherOpenAPI/SecurityRequirement/SecurityRequirementRepresentable.swift @@ -7,19 +7,21 @@ import OpenAPIKit30 -public protocol SecurityRequirementRepresentable: OpenAPISecurityRequirementRepresentable { - - var requirements: [String: [String]] { get } +public protocol SecurityRequirementRepresentable: + OpenAPISecurityRequirementRepresentable +{ + var security: any SecuritySchemeRepresentable { get } + var requirements: [String] { get } } public extension SecurityRequirementRepresentable { + var requirements: [String] { [] } + //[JSONReference: [String]] func openAPISecurityRequirement() -> OpenAPI.SecurityRequirement { - var result: [JSONReference: [String]] = [:] - for (key, value) in self.requirements { - result[.component(named: key)] = value - } - return result + [ + .component(named: security.openAPIIdentifier): requirements + ] } } diff --git a/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeID.swift b/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeID.swift deleted file mode 100644 index 2d48df4..0000000 --- a/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeID.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 23.. -// - -public struct SecuritySchemeID: Sendable, Equatable, Hashable, Codable { - - public var rawValue: String - - public init( - _ rawValue: String - ) { - self.rawValue = rawValue - } -} diff --git a/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeRepresentable.swift b/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeRepresentable.swift index b55fa73..09975bd 100644 --- a/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeRepresentable.swift +++ b/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeRepresentable.swift @@ -9,6 +9,7 @@ import OpenAPIKit30 public protocol SecuritySchemeRepresentable: OpenAPISecuritySchemeRepresentable, + Identifiable, DescriptionProperty, VendorExtensionsProperty { diff --git a/Tests/FeatherOpenAPITests/Example/TestObjects.swift b/Tests/FeatherOpenAPITests/Example/TestObjects.swift index 52819b1..a25d064 100644 --- a/Tests/FeatherOpenAPITests/Example/TestObjects.swift +++ b/Tests/FeatherOpenAPITests/Example/TestObjects.swift @@ -99,9 +99,42 @@ struct TodoCreateOperation: OperationRepresentable { 200: TodoCreateResponse().reference(), ] } + + var security: [any SecurityRequirementRepresentable]? { + [ + OAuthSecurityRequirement(), + APIKeySecurityRequirement(), + ] + } } struct TodoPathItems: PathItemRepresentable { var post: OperationRepresentable? = TodoCreateOperation() } + +struct OAuthSecurityScheme: SecuritySchemeRepresentable { + + var type: OpenAPIKit30.OpenAPI.SecurityScheme.SecurityType = .oauth2( + flows: .init() + ) +} + +struct OAuthSecurityRequirement: SecurityRequirementRepresentable { + + var security: any SecuritySchemeRepresentable = OAuthSecurityScheme() + var requirements: [String] = ["read"] +} + +struct APIKeySecurityScheme: SecuritySchemeRepresentable { + + var type: OpenAPIKit30.OpenAPI.SecurityScheme.SecurityType = .apiKey( + name: "test", + location: .header + ) +} + +struct APIKeySecurityRequirement: SecurityRequirementRepresentable { + + var security: any SecuritySchemeRepresentable = APIKeySecurityScheme() +} From 5e56c0dfa6f0183e54f903c8bc5f2fb706849f37 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sat, 24 Jan 2026 10:17:38 +0100 Subject: [PATCH 22/34] servers --- .../Operation/OperationRepresentable.swift | 7 ++++--- .../FeatherOpenAPITests/Example/TestObjects.swift | 14 ++++++++++++++ .../FeatherOpenAPIKitTests.swift | 8 ++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift index f2e8eb0..de8d193 100644 --- a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift +++ b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift @@ -34,7 +34,7 @@ public protocol OperationRepresentable: var responseMap: ResponseMap { get } var security: [SecurityRequirementRepresentable]? { get } -// var servers: [ServerRepresentable]? + var servers: [ServerRepresentable]? { get } } public extension OperationRepresentable { @@ -47,6 +47,7 @@ public extension OperationRepresentable { var requestBody: RequestBodyRepresentable? { nil } var security: [SecurityRequirementRepresentable]? { nil } + var servers: [ServerRepresentable]? { nil } private var openAPITags: [String]? { tags.isEmpty ? nil : tags.map { $0.name } @@ -74,7 +75,7 @@ public extension OperationRepresentable { callbacks: [:], deprecated: deprecated, security: openAPISecurityRequirements, - servers: nil, + servers: servers?.map { $0.openAPIServer() }, vendorExtensions: vendorExtensions ) } @@ -89,7 +90,7 @@ public extension OperationRepresentable { callbacks: [:], deprecated: deprecated, security: openAPISecurityRequirements, - servers: nil, + servers: servers?.map { $0.openAPIServer() }, vendorExtensions: vendorExtensions ) } diff --git a/Tests/FeatherOpenAPITests/Example/TestObjects.swift b/Tests/FeatherOpenAPITests/Example/TestObjects.swift index a25d064..2d78b56 100644 --- a/Tests/FeatherOpenAPITests/Example/TestObjects.swift +++ b/Tests/FeatherOpenAPITests/Example/TestObjects.swift @@ -106,6 +106,20 @@ struct TodoCreateOperation: OperationRepresentable { APIKeySecurityRequirement(), ] } + + var servers: [any ServerRepresentable]? { + [ + TestServer() + ] + } +} + +extension String: LocationRepresentable { + public var location: String { self } +} + +struct TestServer: ServerRepresentable { + var url: any LocationRepresentable { "http://127.0.0.1:8080/" } } diff --git a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift index 050cf8d..370579d 100644 --- a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift +++ b/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift @@ -21,7 +21,15 @@ struct MyInfo: InfoRepresentable { } struct MyDocument: DocumentRepresentable { + var info: OpenAPIInfoRepresentable + + var servers: [any OpenAPIServerRepresentable] { + [ + TestServer() + ] + } + var paths: PathMap var components: OpenAPIComponentsRepresentable From 4d31b7b898ba0788e3749e55d0a838b94f61053d Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sat, 24 Jan 2026 10:22:00 +0100 Subject: [PATCH 23/34] examples --- .../Components/ComponentsRepresentable.swift | 4 +- .../Example/ExampleRepresentable.swift | 17 +++++--- .../Example/OpenAPIExampleRepresentable.swift | 6 +-- .../ExampleReferenceRepresentable.swift | 41 +++++++++++++++++++ .../Example/TestObjects.swift | 14 ++++--- 5 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 Sources/FeatherOpenAPI/Reference/ExampleReferenceRepresentable.swift diff --git a/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift b/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift index 75c0159..cefe98e 100644 --- a/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift +++ b/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift @@ -57,7 +57,9 @@ public extension ComponentsRepresentable { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in examples { - result[.init(stringLiteral: key.rawValue)] = value.openAPIExample() + if let example = value.openAPIExample().b { + result[.init(stringLiteral: key.rawValue)] = example + } } return result } diff --git a/Sources/FeatherOpenAPI/Example/ExampleRepresentable.swift b/Sources/FeatherOpenAPI/Example/ExampleRepresentable.swift index c933f9a..a0ff853 100644 --- a/Sources/FeatherOpenAPI/Example/ExampleRepresentable.swift +++ b/Sources/FeatherOpenAPI/Example/ExampleRepresentable.swift @@ -9,6 +9,7 @@ import OpenAPIKit30 public protocol ExampleRepresentable: OpenAPIExampleRepresentable, + Identifiable, DescriptionProperty, VendorExtensionsProperty { @@ -17,13 +18,19 @@ public protocol ExampleRepresentable: } public extension ExampleRepresentable { + + func reference() -> ExampleReference { + .init(self) + } - func openAPIExample() -> OpenAPI.Example { + func openAPIExample() -> Either, OpenAPI.Example> { .init( - summary: summary, - description: description, - value: .init(value), - vendorExtensions: vendorExtensions + .init( + summary: summary, + description: description, + value: .init(value), + vendorExtensions: vendorExtensions + ) ) } } diff --git a/Sources/FeatherOpenAPI/Example/OpenAPIExampleRepresentable.swift b/Sources/FeatherOpenAPI/Example/OpenAPIExampleRepresentable.swift index 62ef17f..5c30211 100644 --- a/Sources/FeatherOpenAPI/Example/OpenAPIExampleRepresentable.swift +++ b/Sources/FeatherOpenAPI/Example/OpenAPIExampleRepresentable.swift @@ -8,12 +8,12 @@ import OpenAPIKit30 public protocol OpenAPIExampleRepresentable { - func openAPIExample() -> OpenAPI.Example + func openAPIExample() -> Either, OpenAPI.Example> } extension OpenAPI.Example: OpenAPIExampleRepresentable { - public func openAPIExample() -> OpenAPI.Example { - self + public func openAPIExample() -> Either, OpenAPI.Example> { + .init(self) } } diff --git a/Sources/FeatherOpenAPI/Reference/ExampleReferenceRepresentable.swift b/Sources/FeatherOpenAPI/Reference/ExampleReferenceRepresentable.swift new file mode 100644 index 0000000..5ec3b88 --- /dev/null +++ b/Sources/FeatherOpenAPI/Reference/ExampleReferenceRepresentable.swift @@ -0,0 +1,41 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 24.. +// + +import OpenAPIKit30 + +public protocol ExampleReferenceRepresentable { + var id: ExampleID { get } + var object: ExampleRepresentable { get } +} + +public struct ExampleReference: + ExampleRepresentable, + ExampleReferenceRepresentable +{ + public var summary: String? { object.summary } + public var value: AnyCodable { object.value } + public var description: String? { object.description } + public var vendorExtensions: [String: AnyCodable] { object.vendorExtensions } + + public var object: ExampleRepresentable { + _object + } + + public var id: ExampleID + public var _object: T + + internal init( + _ object: T + ) { + self.id = .init(object.openAPIIdentifier) + self._object = object + } + + public func openAPIExample() -> Either, OpenAPI.Example> { + .reference(.component(named: id.rawValue)) + } +} diff --git a/Tests/FeatherOpenAPITests/Example/TestObjects.swift b/Tests/FeatherOpenAPITests/Example/TestObjects.swift index 2d78b56..e7215ff 100644 --- a/Tests/FeatherOpenAPITests/Example/TestObjects.swift +++ b/Tests/FeatherOpenAPITests/Example/TestObjects.swift @@ -8,6 +8,14 @@ import FeatherOpenAPI import OpenAPIKit30 +extension String: LocationRepresentable { + public var location: String { self } +} + +struct TestServer: ServerRepresentable { + var url: any LocationRepresentable { "http://127.0.0.1:8080/" } +} + struct TodoIDField: IntSchemaRepresentable { var example: Int? { 1 } @@ -114,13 +122,7 @@ struct TodoCreateOperation: OperationRepresentable { } } -extension String: LocationRepresentable { - public var location: String { self } -} -struct TestServer: ServerRepresentable { - var url: any LocationRepresentable { "http://127.0.0.1:8080/" } -} struct TodoPathItems: PathItemRepresentable { From f1be8daff241f8e1f9a6b3040720ed78159fd804 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sat, 24 Jan 2026 10:47:13 +0100 Subject: [PATCH 24/34] petstore tests --- Package.resolved | 6 +- Package.swift | 3 +- .../PathCollectionRepresentable.swift | 4 +- .../Abstraction/SchemaRepresentable.swift | 4 +- .../ApiResponse/ApiResponse+Schemas.swift | 37 ---------- .../Petstore/ApiResponse/ApiResponse.swift | 14 ---- .../Petstore/Category/Category+Schemas.swift | 33 --------- .../Petstore/Category/Category.swift | 13 ---- .../Petstore/Pet/Pet+Operations.swift | 32 --------- .../Petstore/Pet/Pet+Parameters.swift | 21 ------ .../Petstore/Pet/Pet+PathItems.swift | 32 --------- .../Petstore/Pet/Pet+Responses.swift | 32 --------- .../Petstore/Pet/Pet+Schemas.swift | 67 ----------------- .../Petstore/Petstore.swift | 61 ---------------- .../Petstore/Store/Store+Schemas.swift | 61 ---------------- .../Petstore/Tag/Tag+Schemas.swift | 31 -------- .../Petstore/User/User+Schemas.swift | 63 ---------------- .../ApiResponse/ApiResponse+Schemas.swift | 31 ++++++++ .../Petstore/ApiResponse/ApiResponse.swift | 12 ++++ .../Petstore/Category/Category+Schemas.swift | 29 ++++++++ .../Petstore/Category/Category.swift | 12 ++++ .../Petstore/Pet/Pet+Operations.swift | 29 ++++++++ .../Petstore/Pet/Pet+Parameters.swift | 20 ++++++ .../Petstore/Pet/Pet+PathItems.swift | 18 +++++ .../Petstore/Pet/Pet+Responses.swift | 22 ++++++ .../Petstore/Pet/Pet+Schemas.swift | 56 +++++++++++++++ .../Petstore/Pet/Pet+Tags.swift | 9 +-- .../Petstore/Pet/Pet.swift | 5 +- .../Petstore/Petstore.swift | 71 +++++++++++++++++++ .../Petstore/Store/Store+Schemas.swift | 55 ++++++++++++++ .../Petstore/Store/Store.swift | 5 +- .../Petstore/Tag/Tag+Schemas.swift | 27 +++++++ .../Petstore/Tag/Tag.swift | 5 +- .../Petstore/User/User+Schemas.swift | 60 ++++++++++++++++ .../Petstore/User/User.swift | 5 +- .../PetstoreOpenAPIKitTests.swift | 2 +- .../{Example => Todo}/TestObjects.swift | 0 docker/tests/Dockerfile | 2 +- 38 files changed, 465 insertions(+), 524 deletions(-) delete mode 100644 Tests/FeatherOpenAPIKitPluginTests/Petstore/ApiResponse/ApiResponse+Schemas.swift delete mode 100644 Tests/FeatherOpenAPIKitPluginTests/Petstore/ApiResponse/ApiResponse.swift delete mode 100644 Tests/FeatherOpenAPIKitPluginTests/Petstore/Category/Category+Schemas.swift delete mode 100644 Tests/FeatherOpenAPIKitPluginTests/Petstore/Category/Category.swift delete mode 100644 Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Operations.swift delete mode 100644 Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Parameters.swift delete mode 100644 Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+PathItems.swift delete mode 100644 Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Responses.swift delete mode 100644 Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Schemas.swift delete mode 100644 Tests/FeatherOpenAPIKitPluginTests/Petstore/Petstore.swift delete mode 100644 Tests/FeatherOpenAPIKitPluginTests/Petstore/Store/Store+Schemas.swift delete mode 100644 Tests/FeatherOpenAPIKitPluginTests/Petstore/Tag/Tag+Schemas.swift delete mode 100644 Tests/FeatherOpenAPIKitPluginTests/Petstore/User/User+Schemas.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse+Schemas.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/Category/Category+Schemas.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/Category/Category.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Operations.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Parameters.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/Pet/Pet+PathItems.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Responses.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Schemas.swift rename Tests/{FeatherOpenAPIKitPluginTests => FeatherOpenAPITests}/Petstore/Pet/Pet+Tags.swift (53%) rename Tests/{FeatherOpenAPIKitPluginTests => FeatherOpenAPITests}/Petstore/Pet/Pet.swift (68%) create mode 100644 Tests/FeatherOpenAPITests/Petstore/Petstore.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/Store/Store+Schemas.swift rename Tests/{FeatherOpenAPIKitPluginTests => FeatherOpenAPITests}/Petstore/Store/Store.swift (67%) create mode 100644 Tests/FeatherOpenAPITests/Petstore/Tag/Tag+Schemas.swift rename Tests/{FeatherOpenAPIKitPluginTests => FeatherOpenAPITests}/Petstore/Tag/Tag.swift (68%) create mode 100644 Tests/FeatherOpenAPITests/Petstore/User/User+Schemas.swift rename Tests/{FeatherOpenAPIKitPluginTests => FeatherOpenAPITests}/Petstore/User/User.swift (68%) rename Tests/{FeatherOpenAPIKitPluginTests => FeatherOpenAPITests}/PetstoreOpenAPIKitTests.swift (95%) rename Tests/FeatherOpenAPITests/{Example => Todo}/TestObjects.swift (100%) diff --git a/Package.resolved b/Package.resolved index cff3abd..79c9792 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,13 +1,13 @@ { - "originHash" : "00017e3ffac1735a2e9564fd12a07204929f5647630eb11ad464a2d3aca034bc", + "originHash" : "0c7d07317a3d86a614e3f4417365a7e94201f8451d0a7a96d9b66edc25ca7c6b", "pins" : [ { "identity" : "openapikit", "kind" : "remoteSourceControl", "location" : "https://github.com/mattpolzin/OpenAPIKit", "state" : { - "revision" : "d267e29373b3d2ff395d3b46d8bf2085a9263f67", - "version" : "5.0.0-rc.2" + "revision" : "b71ac7b811af8a7ef9b3a1cfb20e9cff96f86451", + "version" : "4.3.1" } }, { diff --git a/Package.swift b/Package.swift index 2c987c9..9ca5245 100644 --- a/Package.swift +++ b/Package.swift @@ -34,7 +34,8 @@ let package = Package( .library(name: "FeatherOpenAPI", targets: ["FeatherOpenAPI"]), ], dependencies: [ - .package(url: "https://github.com/mattpolzin/OpenAPIKit", exact: "5.0.0-rc.2"), +// .package(url: "https://github.com/mattpolzin/OpenAPIKit", exact: "5.0.0-rc.2"), + .package(url: "https://github.com/mattpolzin/OpenAPIKit", from: "4.3.0"), .package(url: "https://github.com/jpsim/Yams", from: "6.2.0"), ], targets: [ diff --git a/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift b/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift index 6b8c695..cfeb2a1 100644 --- a/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift +++ b/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift @@ -20,7 +20,7 @@ public protocol PathCollectionRepresentable: var components: FeatherOpenAPI.Components { get } } -extension PathCollectionRepresentable { +public extension PathCollectionRepresentable { var referencedSchemaMap: OrderedDictionary { var results = OrderedDictionary() @@ -91,7 +91,7 @@ extension PathCollectionRepresentable { pathMap.values.map { $0.referencedTags }.flatMap { $0 } } - public var referencedSecurityRequirements: [SecurityRequirementRepresentable] { + var referencedSecurityRequirements: [SecurityRequirementRepresentable] { pathMap.values.map { $0.referencedSecurityRequirements }.flatMap { $0 } } diff --git a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift index 45d7642..cc35c39 100644 --- a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift @@ -22,7 +22,9 @@ public protocol SchemaRepresentable: public extension SchemaRepresentable { - func reference() -> SchemaReference { + func reference( + required: Bool = true + ) -> SchemaReference { .init(self) } diff --git a/Tests/FeatherOpenAPIKitPluginTests/Petstore/ApiResponse/ApiResponse+Schemas.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/ApiResponse/ApiResponse+Schemas.swift deleted file mode 100644 index a352222..0000000 --- a/Tests/FeatherOpenAPIKitPluginTests/Petstore/ApiResponse/ApiResponse+Schemas.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 22.. -// - -import FeatherOpenAPIKit - -extension Petstore.ApiResponse { - - enum Schemas { - - enum ApiResponse: ObjectSchema { - - enum Code: Int32Schema { - } - - enum `ResponseType`: TextSchema { - - } - - enum Message: TextSchema { - - } - - static var properties: [ObjectSchemaProperty] { - [ - .init("code", Code.self), - .init("type", `ResponseType`.self), - .init("message", Message.self), - ] - } - } - - } -} diff --git a/Tests/FeatherOpenAPIKitPluginTests/Petstore/ApiResponse/ApiResponse.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/ApiResponse/ApiResponse.swift deleted file mode 100644 index b8c8f7f..0000000 --- a/Tests/FeatherOpenAPIKitPluginTests/Petstore/ApiResponse/ApiResponse.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 22.. -// - -import FeatherOpenAPIKit - -extension Petstore { - - enum ApiResponse: Component { - } -} diff --git a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Category/Category+Schemas.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Category/Category+Schemas.swift deleted file mode 100644 index 2dc86ea..0000000 --- a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Category/Category+Schemas.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 22.. -// - -import FeatherOpenAPIKit - -extension Petstore.Category { - - enum Schemas { - - enum Category: ObjectSchema { - - enum Id: Int64Schema { - static var example: Int? { 1 } - } - - enum Name: TextSchema { - static var example: String? { "Dogs" } - } - - static var properties: [ObjectSchemaProperty] { - [ - .init("id", Id.self), - .init("name", Name.self), - ] - } - - } - } -} diff --git a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Category/Category.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Category/Category.swift deleted file mode 100644 index 7baebe9..0000000 --- a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Category/Category.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 22.. -// - -import FeatherOpenAPIKit - -extension Petstore { - - enum Category: Component {} -} diff --git a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Operations.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Operations.swift deleted file mode 100644 index a13a2ab..0000000 --- a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Operations.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 22.. -// - -import FeatherOpenAPIKit - -extension Petstore.Pet { - - enum Operations { - - enum Get: Operation { - static var tag: Tag.Type { Tags.Pet.self } - static let summary = "Update an existing pet." - static let description = "Update an existing pet by Id." - static var operationId: String { "updatePet" } - static var parameters: [Parameter.Type] { - [ - Parameters.Id.self - ] - } - static var responses: [OperationResponse] { - [ - .init(200, Responses.Detail.self) - ] - } - } - - } -} diff --git a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Parameters.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Parameters.swift deleted file mode 100644 index d44c33a..0000000 --- a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Parameters.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 22.. -// - -import FeatherOpenAPIKit - -extension Petstore.Pet { - - enum Parameters { - - enum Id: PathParameter { - static let name = "petId" - static let description = "ID of pet to return" - static var required: Bool { true } - static var schema: Schema.Type { Schemas.Pet.Id.self } - } - } -} diff --git a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+PathItems.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+PathItems.swift deleted file mode 100644 index 5d6446f..0000000 --- a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+PathItems.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 22.. -// - -import FeatherOpenAPIKit - -extension Petstore.Pet { - - enum PathItems { - - enum Main: PathItem { - static var path: Path { "/pet" } - // - // static var post: Operation.Type? { Operations.Create.self } - } - - enum Identified: PathItem { - static var path: Path { Main.path / Parameters.Id.path } - static var parameters: [Parameter.Type] { - [ - Parameters.Id.self - ] - } - - static var get: Operation.Type? { Operations.Get.self } - } - - } -} diff --git a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Responses.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Responses.swift deleted file mode 100644 index 44f7f99..0000000 --- a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Responses.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 22.. -// - -import FeatherOpenAPIKit -import OpenAPIKit30 - -extension Petstore.Pet { - - enum Responses { - - enum Detail: Response { - static let description = "Successful operation" - // static var headers: [Header.Type] { - // [ - // Headers.CustomResponseHeader.self - // ] - // } - // static var schema: Schema.Type { Schemas.Pet.self } - - static var contents: [OpenAPI.ContentType: any Schema.Type] { - [ - .json: Schemas.Pet.self, - .xml: Schemas.Pet.self, - ] - } - } - } -} diff --git a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Schemas.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Schemas.swift deleted file mode 100644 index a24aeba..0000000 --- a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Schemas.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 22.. -// - -import FeatherOpenAPIKit - -extension Petstore.Pet { - - enum Schemas { - - enum Pet: ObjectSchema { - - enum Id: Int64Schema { - - static var example: Int? { 10 } - } - - enum Name: TextSchema { - static var example: String? { "doggie" } - } - - enum PhotoUrls: ArraySchema { - - enum Item: TextSchema { - - } - - static var items: any Schema.Type { Item.self } - } - - enum Tags: ArraySchema { - static var items: any Schema.Type { - Petstore.Tag.Schemas.Tag.self - } - } - - enum Status: EnumSchema { - static var description: String { "pet status in the store" } - static var allowedValues: [String] { - [ - "available", - "pending", - "sold", - ] - } - } - - static var properties: [ObjectSchemaProperty] { - [ - .init("id", Id.self, required: false), - .init("name", Name.self), - .init( - "category", - Petstore.Category.Schemas.Category.self, - required: false - ), - .init("photoUrls", PhotoUrls.self), - .init("tags", Tags.self, required: false), - .init("status", Status.self, required: false), - ] - } - } - } -} diff --git a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Petstore.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Petstore.swift deleted file mode 100644 index c1d2a85..0000000 --- a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Petstore.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 22.. -// - -import FeatherOpenAPIKit -import OpenAPIKit30 -import Foundation - -enum Petstore {} - -// https://petstore3.swagger.io/ -// https://petstore3.swagger.io/api/v3/openapi.json -// https://petstore3.swagger.io/api/v3/openapi.yaml - -struct PetstoreDocument: Document { - - let components: [Component.Type] - - init() { - self.components = [ - Petstore.User.self, - Petstore.Store.self, - Petstore.Pet.self, - Petstore.ApiResponse.self, - Petstore.Category.self, - Petstore.Tag.self, - ] - } - - func openAPIDocument() throws -> OpenAPI.Document { - try composedDocument( - info: .init( - title: "Swagger Petstore - OpenAPI 3.0", - description: """ - This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about\nSwagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.\n\nSome useful links:\n- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)\n- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml) - """, - termsOfService: .init(string: "https://swagger.io/terms/")!, - contact: .init( - email: "apiteam@swagger.io" - ), - license: .init( - name: "Apache 2.0", - url: .init( - string: - "https://www.apache.org/licenses/LICENSE-2.0.html" - )! - ), - version: "1.0.27", - ), - // TODO: external docs support. - servers: [ - .init( - url: .init(string: "/api/v3")! - ) - ], - ) - } -} diff --git a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Store/Store+Schemas.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Store/Store+Schemas.swift deleted file mode 100644 index 33e1951..0000000 --- a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Store/Store+Schemas.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 22.. -// - -import FeatherOpenAPIKit - -extension Petstore.Store { - - enum Schemas { - - enum Order: ObjectSchema { - - enum Id: Int64Schema { - static var example: Int? { 10 } - } - - enum PetId: Int64Schema { - static var example: Int? { 198772 } - } - - enum Quantity: Int32Schema { - static var example: Int? { 7 } - } - - enum ShipDate: DateTimeSchema { - static var example: String? { nil } - } - - enum Status: EnumSchema { - static var description: String { "Order Status" } - static var allowedValues: [String] { - [ - "placed", - "approved", - "delivered", - ] - } - static var defaultValue: String? { nil } - static var example: String? { "approved" } - } - - enum Complete: BooleanSchema { - } - - static var properties: [ObjectSchemaProperty] { - [ - .init("id", Id.self), - .init("petId", PetId.self), - .init("quantity", Quantity.self), - .init("shipDate", ShipDate.self), - .init("status", Status.self), - .init("complete", Complete.self), - ] - } - } - - } -} diff --git a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Tag/Tag+Schemas.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/Tag/Tag+Schemas.swift deleted file mode 100644 index d946d7d..0000000 --- a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Tag/Tag+Schemas.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 22.. -// - -import FeatherOpenAPIKit - -extension Petstore.Tag { - - enum Schemas { - - enum Tag: ObjectSchema { - - enum Id: Int64Schema { - } - - enum Name: TextSchema { - - } - - static var properties: [ObjectSchemaProperty] { - [ - .init("id", Id.self), - .init("name", Name.self), - ] - } - } - } -} diff --git a/Tests/FeatherOpenAPIKitPluginTests/Petstore/User/User+Schemas.swift b/Tests/FeatherOpenAPIKitPluginTests/Petstore/User/User+Schemas.swift deleted file mode 100644 index fe6f17a..0000000 --- a/Tests/FeatherOpenAPIKitPluginTests/Petstore/User/User+Schemas.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// File.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 22.. -// - -import FeatherOpenAPIKit - -extension Petstore.User { - - enum Schemas { - - enum User: ObjectSchema { - - enum Id: Int64Schema { - static var example: Int? { 0 } - } - - enum UserName: TextSchema { - static var example: String? { "theUser" } - } - - enum FirstName: TextSchema { - static var example: String? { "John" } - } - - enum LastName: TextSchema { - static var example: String? { "James" } - } - - enum Email: EmailSchema { - static var example: String? { "john@email.com" } - } - - enum Password: PasswordSchema { - static var example: String? { "12345" } - } - - enum Phone: TextSchema { - static var example: String? { "12345" } - } - - enum UserStatus: Int32Schema { - static var description: String? { "User Status" } - static var example: Int? { 1 } - } - - static var properties: [ObjectSchemaProperty] { - [ - .init("id", Id.self), - .init("username", UserName.self), - .init("firstName", FirstName.self), - .init("lastName", LastName.self), - .init("email", Email.self), - .init("password", Password.self), - .init("phone", Phone.self), - .init("userStatus", UserStatus.self), - ] - } - } - } -} diff --git a/Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse+Schemas.swift b/Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse+Schemas.swift new file mode 100644 index 0000000..156c18d --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse+Schemas.swift @@ -0,0 +1,31 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI +import OpenAPIKit30 + +extension Petstore.ApiResponse { + + struct CodeSchema: Int32SchemaRepresentable { + } + + struct ResponseTypeSchema: StringSchemaRepresentable { + } + + struct MessageSchema: StringSchemaRepresentable { + } + + struct ApiResponseSchema: ObjectSchemaRepresentable { + var propertyMap: SchemaMap { + [ + "code": CodeSchema(), + "type": ResponseTypeSchema(), + "message": MessageSchema(), + ] + } + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse.swift b/Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse.swift new file mode 100644 index 0000000..9ec90c1 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse.swift @@ -0,0 +1,12 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore { + enum ApiResponse {} +} diff --git a/Tests/FeatherOpenAPITests/Petstore/Category/Category+Schemas.swift b/Tests/FeatherOpenAPITests/Petstore/Category/Category+Schemas.swift new file mode 100644 index 0000000..121c4c0 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/Category/Category+Schemas.swift @@ -0,0 +1,29 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI +import OpenAPIKit30 + +extension Petstore.Category { + + struct IdSchema: Int64SchemaRepresentable { + var example: Int64? { 1 } + } + + struct NameSchema: StringSchemaRepresentable { + var example: String? { "Dogs" } + } + + struct CategorySchema: ObjectSchemaRepresentable { + var propertyMap: SchemaMap { + [ + "id": IdSchema(), + "name": NameSchema(), + ] + } + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/Category/Category.swift b/Tests/FeatherOpenAPITests/Petstore/Category/Category.swift new file mode 100644 index 0000000..12374dd --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/Category/Category.swift @@ -0,0 +1,12 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore { + enum Category {} +} diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Operations.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Operations.swift new file mode 100644 index 0000000..0682d39 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Operations.swift @@ -0,0 +1,29 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI +import OpenAPIKit30 + +extension Petstore.Pet { + + struct GetOperation: OperationRepresentable { + var tags: [TagRepresentable] { [PetTag()] } + var summary: String? { "Update an existing pet." } + var description: String? { "Update an existing pet by Id." } + var operationId: String? { "updatePet" } + var parameters: [ParameterRepresentable] { + [ + IdParameter().reference(), + ] + } + var responseMap: ResponseMap { + [ + 200: DetailResponse().reference(), + ] + } + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Parameters.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Parameters.swift new file mode 100644 index 0000000..2eb3463 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Parameters.swift @@ -0,0 +1,20 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore.Pet { + + struct IdParameter: PathParameterRepresentable { + var name: String { "petId" } + var description: String? { "ID of pet to return" } + var required: Bool { true } + var schema: any OpenAPISchemaRepresentable { + IdSchema().reference() + } + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+PathItems.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+PathItems.swift new file mode 100644 index 0000000..fef6a82 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+PathItems.swift @@ -0,0 +1,18 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore.Pet { + + struct MainPathItem: PathItemRepresentable { + } + + struct IdentifiedPathItem: PathItemRepresentable { + var get: OperationRepresentable? { GetOperation() } + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Responses.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Responses.swift new file mode 100644 index 0000000..1112d95 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Responses.swift @@ -0,0 +1,22 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI +import OpenAPIKit30 + +extension Petstore.Pet { + + struct DetailResponse: ResponseRepresentable { + var description: String { "Successful operation" } + var contentMap: ContentMap { + [ + .json: Content(Schema()), + .xml: Content(Schema()), + ] + } + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Schemas.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Schemas.swift new file mode 100644 index 0000000..17abce0 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Schemas.swift @@ -0,0 +1,56 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI +import OpenAPIKit30 + +extension Petstore.Pet { + + struct IdSchema: Int64SchemaRepresentable { + var example: Int64? { 10 } + } + + struct NameSchema: StringSchemaRepresentable { + var example: String? { "doggie" } + } + + struct PhotoUrlItemSchema: StringSchemaRepresentable { + } + + struct PhotoUrlsSchema: ArraySchemaRepresentable { + var items: JSONSchema? { PhotoUrlItemSchema().openAPISchema() } + } + + struct TagsSchema: ArraySchemaRepresentable { + var required: Bool { false } + var items: JSONSchema? { Petstore.Tag.TagSchema().openAPISchema() } + } + + struct StatusSchema: StringSchemaRepresentable { + var description: String? { "pet status in the store" } + var allowedValues: [String]? { + [ + "available", + "pending", + "sold", + ] + } + } + + struct Schema: ObjectSchemaRepresentable { + var propertyMap: SchemaMap { + [ + "id": IdSchema().reference(required: false), + "name": NameSchema(), + "category": Petstore.Category.CategorySchema().reference(required: false), + "photoUrls": PhotoUrlsSchema(), + "tags": TagsSchema(), + "status": StatusSchema().reference(required: false), + ] + } + } +} diff --git a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Tags.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Tags.swift similarity index 53% rename from Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Tags.swift rename to Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Tags.swift index 4b7af69..4da45db 100644 --- a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet+Tags.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Tags.swift @@ -5,14 +5,11 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension Petstore.Pet { - enum Tags { - - enum Pet: Tag { - static let name = "Pet" - } + struct PetTag: TagRepresentable { + var name: String { "Pet" } } } diff --git a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet.swift similarity index 68% rename from Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet.swift rename to Tests/FeatherOpenAPITests/Petstore/Pet/Pet.swift index 16d3158..3526038 100644 --- a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Pet/Pet.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet.swift @@ -5,9 +5,8 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension Petstore { - - enum Pet: Component {} + enum Pet {} } diff --git a/Tests/FeatherOpenAPITests/Petstore/Petstore.swift b/Tests/FeatherOpenAPITests/Petstore/Petstore.swift new file mode 100644 index 0000000..dc9c033 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/Petstore.swift @@ -0,0 +1,71 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI +import OpenAPIKit30 + +enum Petstore {} + +struct PetstoreLocation: LocationRepresentable { + let location: String +} + +struct PetstoreContact: ContactRepresentable { + var email: String? { "apiteam@swagger.io" } +} + +struct PetstoreLicense: LicenseRepresentable { + var name: String { "Apache 2.0" } + var url: LocationRepresentable? { + PetstoreLocation(location: "https://www.apache.org/licenses/LICENSE-2.0.html") + } +} + +struct PetstoreInfo: InfoRepresentable { + var title: String { "Swagger Petstore - OpenAPI 3.0" } + var description: String? { + """ + This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about + Swagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we've switched to the design first approach! + You can now help us improve the API whether it's by making changes to the definition itself or to the code. + That way, with time, we can improve the API in general, and expose some of the new features in OAS3. + + Some useful links: + - [The Pet Store repository](https://github.com/swagger-api/swagger-petstore) + - [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml) + """ + } + var termsOfService: LocationRepresentable? { + PetstoreLocation(location: "https://swagger.io/terms/") + } + var contact: OpenAPIContactRepresentable? { PetstoreContact() } + var license: OpenAPILicenseRepresentable? { PetstoreLicense() } + var version: String { "1.0.27" } +} + +struct PetstoreServer: ServerRepresentable { + var url: LocationRepresentable { PetstoreLocation(location: "/api/v3") } +} + +struct PetstorePathCollection: PathCollectionRepresentable { + var pathMap: PathMap { + [ + "pet": Petstore.Pet.MainPathItem(), + "pet/{petId}": Petstore.Pet.IdentifiedPathItem(), + ] + } +} + +struct PetstoreDocument: DocumentRepresentable { + let collection = PetstorePathCollection() + + var info: OpenAPIInfoRepresentable { PetstoreInfo() } + var servers: [OpenAPIServerRepresentable] { [PetstoreServer()] } + + var paths: PathMap { collection.pathMap } + var components: OpenAPIComponentsRepresentable { collection.components } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/Store/Store+Schemas.swift b/Tests/FeatherOpenAPITests/Petstore/Store/Store+Schemas.swift new file mode 100644 index 0000000..ee0ab57 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/Store/Store+Schemas.swift @@ -0,0 +1,55 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI +import OpenAPIKit30 + +extension Petstore.Store { + + struct OrderIdSchema: Int64SchemaRepresentable { + var example: Int64? { 10 } + } + + struct OrderPetIdSchema: Int64SchemaRepresentable { + var example: Int64? { 198772 } + } + + struct OrderQuantitySchema: Int32SchemaRepresentable { + var example: Int32? { 7 } + } + + struct OrderShipDateSchema: StringSchemaRepresentable { + } + + struct OrderStatusSchema: StringSchemaRepresentable { + var description: String? { "Order Status" } + var allowedValues: [String]? { + [ + "placed", + "approved", + "delivered", + ] + } + var example: String? { "approved" } + } + + struct OrderCompleteSchema: BoolSchemaRepresentable { + } + + struct OrderSchema: ObjectSchemaRepresentable { + var propertyMap: SchemaMap { + [ + "id": OrderIdSchema(), + "petId": OrderPetIdSchema(), + "quantity": OrderQuantitySchema(), + "shipDate": OrderShipDateSchema(), + "status": OrderStatusSchema(), + "complete": OrderCompleteSchema(), + ] + } + } +} diff --git a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Store/Store.swift b/Tests/FeatherOpenAPITests/Petstore/Store/Store.swift similarity index 67% rename from Tests/FeatherOpenAPIKitPluginTests/Petstore/Store/Store.swift rename to Tests/FeatherOpenAPITests/Petstore/Store/Store.swift index f5dca68..3bd691d 100644 --- a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Store/Store.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Store/Store.swift @@ -5,9 +5,8 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension Petstore { - - enum Store: Component {} + enum Store {} } diff --git a/Tests/FeatherOpenAPITests/Petstore/Tag/Tag+Schemas.swift b/Tests/FeatherOpenAPITests/Petstore/Tag/Tag+Schemas.swift new file mode 100644 index 0000000..44db974 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/Tag/Tag+Schemas.swift @@ -0,0 +1,27 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI +import OpenAPIKit30 + +extension Petstore.Tag { + + struct IdSchema: Int64SchemaRepresentable { + } + + struct NameSchema: StringSchemaRepresentable { + } + + struct TagSchema: ObjectSchemaRepresentable { + var propertyMap: SchemaMap { + [ + "id": IdSchema(), + "name": NameSchema(), + ] + } + } +} diff --git a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Tag/Tag.swift b/Tests/FeatherOpenAPITests/Petstore/Tag/Tag.swift similarity index 68% rename from Tests/FeatherOpenAPIKitPluginTests/Petstore/Tag/Tag.swift rename to Tests/FeatherOpenAPITests/Petstore/Tag/Tag.swift index ccfb01f..0278d68 100644 --- a/Tests/FeatherOpenAPIKitPluginTests/Petstore/Tag/Tag.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Tag/Tag.swift @@ -5,9 +5,8 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension Petstore { - - enum Tag: Component {} + enum Tag {} } diff --git a/Tests/FeatherOpenAPITests/Petstore/User/User+Schemas.swift b/Tests/FeatherOpenAPITests/Petstore/User/User+Schemas.swift new file mode 100644 index 0000000..298328b --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/User/User+Schemas.swift @@ -0,0 +1,60 @@ +// +// File.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI +import OpenAPIKit30 + +extension Petstore.User { + + struct UserIdSchema: Int64SchemaRepresentable { + var example: Int64? { 0 } + } + + struct UserNameSchema: StringSchemaRepresentable { + var example: String? { "theUser" } + } + + struct UserFirstNameSchema: StringSchemaRepresentable { + var example: String? { "John" } + } + + struct UserLastNameSchema: StringSchemaRepresentable { + var example: String? { "James" } + } + + struct UserEmailSchema: StringSchemaRepresentable { + var example: String? { "john@email.com" } + } + + struct UserPasswordSchema: StringSchemaRepresentable { + var example: String? { "12345" } + } + + struct UserPhoneSchema: StringSchemaRepresentable { + var example: String? { "12345" } + } + + struct UserStatusSchema: Int32SchemaRepresentable { + var description: String? { "User Status" } + var example: Int32? { 1 } + } + + struct UserSchema: ObjectSchemaRepresentable { + var propertyMap: SchemaMap { + [ + "id": UserIdSchema(), + "username": UserNameSchema(), + "firstName": UserFirstNameSchema(), + "lastName": UserLastNameSchema(), + "email": UserEmailSchema(), + "password": UserPasswordSchema(), + "phone": UserPhoneSchema(), + "userStatus": UserStatusSchema(), + ] + } + } +} diff --git a/Tests/FeatherOpenAPIKitPluginTests/Petstore/User/User.swift b/Tests/FeatherOpenAPITests/Petstore/User/User.swift similarity index 68% rename from Tests/FeatherOpenAPIKitPluginTests/Petstore/User/User.swift rename to Tests/FeatherOpenAPITests/Petstore/User/User.swift index e3c244d..1d048b4 100644 --- a/Tests/FeatherOpenAPIKitPluginTests/Petstore/User/User.swift +++ b/Tests/FeatherOpenAPITests/Petstore/User/User.swift @@ -5,9 +5,8 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension Petstore { - - enum User: Component {} + enum User {} } diff --git a/Tests/FeatherOpenAPIKitPluginTests/PetstoreOpenAPIKitTests.swift b/Tests/FeatherOpenAPITests/PetstoreOpenAPIKitTests.swift similarity index 95% rename from Tests/FeatherOpenAPIKitPluginTests/PetstoreOpenAPIKitTests.swift rename to Tests/FeatherOpenAPITests/PetstoreOpenAPIKitTests.swift index 1108e98..f6e2035 100644 --- a/Tests/FeatherOpenAPIKitPluginTests/PetstoreOpenAPIKitTests.swift +++ b/Tests/FeatherOpenAPITests/PetstoreOpenAPIKitTests.swift @@ -11,7 +11,7 @@ import OpenAPIKitCore import Yams import Testing -@testable import FeatherOpenAPIKit +@testable import FeatherOpenAPI @Suite struct PetstoreOpenAPIKitTests { diff --git a/Tests/FeatherOpenAPITests/Example/TestObjects.swift b/Tests/FeatherOpenAPITests/Todo/TestObjects.swift similarity index 100% rename from Tests/FeatherOpenAPITests/Example/TestObjects.swift rename to Tests/FeatherOpenAPITests/Todo/TestObjects.swift diff --git a/docker/tests/Dockerfile b/docker/tests/Dockerfile index 73be175..8fa3bbf 100644 --- a/docker/tests/Dockerfile +++ b/docker/tests/Dockerfile @@ -1,4 +1,4 @@ -FROM swift:6.1 +FROM swift:6.2 WORKDIR /app From 09d169ae608548cff66cf990d4f97aa02b07b1f3 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sat, 24 Jan 2026 10:55:33 +0100 Subject: [PATCH 25/34] remove openapikit files --- .../Operation/OperationRepresentable.swift | 24 ++ .../Schema => }/DateTimeSchema.swift | 0 .../Extensions/JSONSchema+Enumeration.swift | 26 --- .../Extensions/JSONSchema+Text.swift | 23 -- .../String+LowercasedFirstLetter.swift | 16 -- .../Interfaces/Component.swift | 43 ---- .../Interfaces/Document.swift | 208 ------------------ .../Interfaces/Header/Header.swift | 30 --- .../Interfaces/Identifiable.swift | 32 --- .../Interfaces/Operation/Operation.swift | 61 ----- .../Operation/OperationResponse.swift | 79 ------- .../Parameter/HeaderParameter.swift | 29 --- .../Interfaces/Parameter/Parameter.swift | 36 --- .../Interfaces/Parameter/PathParameter.swift | 21 -- .../Interfaces/Parameter/QueryParameter.swift | 31 --- .../Interfaces/PathItem/PathItem.swift | 67 ------ .../Interfaces/RequestBody/BinaryBody.swift | 28 --- .../Interfaces/RequestBody/FormBody.swift | 32 --- .../Interfaces/RequestBody/JSONBody.swift | 33 --- .../Interfaces/RequestBody/RequestBody.swift | 18 -- .../Interfaces/Response/BinaryResponse.swift | 22 -- .../Interfaces/Response/JSONResponse.swift | 22 -- .../Interfaces/Response/Response.swift | 54 ----- .../Interfaces/Schema/ArraySchema.swift | 31 --- .../Interfaces/Schema/BooleanSchema.swift | 18 -- .../Interfaces/Schema/DoubleSchema.swift | 20 -- .../Interfaces/Schema/EmailSchema.swift | 16 -- .../Interfaces/Schema/EnumSchema.swift | 35 --- .../Interfaces/Schema/FloatSchema.swift | 20 -- .../Interfaces/Schema/IDSchema.swift | 16 -- .../Interfaces/Schema/Int32Schema.swift | 23 -- .../Interfaces/Schema/Int64Schema.swift | 22 -- .../Interfaces/Schema/IntSchema.swift | 20 -- .../Interfaces/Schema/NanoIDSchema.swift | 27 --- .../Interfaces/Schema/ObjectSchema.swift | 24 -- .../Schema/ObjectSchemaProperty.swift | 25 --- .../Interfaces/Schema/PasswordSchema.swift | 16 -- .../Interfaces/Schema/Schema.swift | 32 --- .../Interfaces/Schema/TextSchema.swift | 26 --- .../Interfaces/Schema/UUIDSchema.swift | 28 --- .../SecurityScheme/SecurityScheme.swift | 31 --- .../Interfaces/Tag/Tag.swift | 27 --- .../Schema => }/NumberSchema.swift | 0 .../{Interfaces/PathItem => }/Path.swift | 0 .../Example/Model/ExampleModel+Headers.swift | 18 +- .../Model/ExampleModel+Operations.swift | 68 +++--- .../Model/ExampleModel+Parameters.swift | 30 +-- .../Model/ExampleModel+PathItems.swift | 30 +-- .../Model/ExampleModel+RequestBodies.swift | 17 +- .../Model/ExampleModel+Responses.swift | 39 ++-- .../Example/Model/ExampleModel+Schemas.swift | 128 +++++------ .../Model/ExampleModel+SecuritySchemes.swift | 38 +--- .../Example/Model/ExampleModel+Tags.swift | 15 +- .../Example/Model/ExampleModel.swift | 24 +- .../ExampleDuplicatedItemModel+Schemas.swift | 35 +-- .../Model/ExampleDuplicatedItemModel.swift | 17 +- ...xampleMissingParentItemModel+Schemas.swift | 24 +- .../Model/ExampleMissingParentItemModel.swift | 16 +- .../Example/ExampleDocument.swift | 86 +++++--- .../ExampleDuplicatedItemDocument.swift | 58 ++--- .../ExampleMissingParentItemDocument.swift | 56 +++-- docker/tests/Dockerfile | 2 +- 62 files changed, 293 insertions(+), 1800 deletions(-) rename Sources/FeatherOpenAPIKit/{Interfaces/Schema => }/DateTimeSchema.swift (100%) delete mode 100644 Sources/FeatherOpenAPIKit/Extensions/JSONSchema+Enumeration.swift delete mode 100644 Sources/FeatherOpenAPIKit/Extensions/JSONSchema+Text.swift delete mode 100644 Sources/FeatherOpenAPIKit/Extensions/String+LowercasedFirstLetter.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Component.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Document.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Header/Header.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Identifiable.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Operation/Operation.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Operation/OperationResponse.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Parameter/HeaderParameter.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Parameter/Parameter.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Parameter/PathParameter.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Parameter/QueryParameter.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/PathItem/PathItem.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/RequestBody/BinaryBody.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/RequestBody/FormBody.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/RequestBody/JSONBody.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/RequestBody/RequestBody.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Response/BinaryResponse.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Response/JSONResponse.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Response/Response.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Schema/ArraySchema.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Schema/BooleanSchema.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Schema/DoubleSchema.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Schema/EmailSchema.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Schema/EnumSchema.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Schema/FloatSchema.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Schema/IDSchema.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Schema/Int32Schema.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Schema/Int64Schema.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Schema/IntSchema.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Schema/NanoIDSchema.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Schema/ObjectSchema.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Schema/ObjectSchemaProperty.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Schema/PasswordSchema.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Schema/Schema.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Schema/TextSchema.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Schema/UUIDSchema.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/SecurityScheme/SecurityScheme.swift delete mode 100644 Sources/FeatherOpenAPIKit/Interfaces/Tag/Tag.swift rename Sources/FeatherOpenAPIKit/{Interfaces/Schema => }/NumberSchema.swift (100%) rename Sources/FeatherOpenAPIKit/{Interfaces/PathItem => }/Path.swift (100%) diff --git a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift index de8d193..4d0e405 100644 --- a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift +++ b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift @@ -7,6 +7,16 @@ import OpenAPIKit30 +fileprivate extension String { + + func lowercasedFirstLetter() -> String { + guard !isEmpty else { + return self + } + return prefix(1).lowercased() + dropFirst() + } +} + public protocol OperationRepresentable: OpenAPIOperationRepresentable, @@ -44,6 +54,20 @@ public extension OperationRepresentable { var operationId: String? { nil } var parameters: [ParameterRepresentable] { [] } + + static var operationId: String { + var components = String(reflecting: self) + .split(separator: ".") + .dropFirst() + .map(String.init) + + components.remove(at: 2) + if let last = components.popLast()?.lowercasedFirstLetter() { + components.insert(last, at: 0) + } + return components.joined(separator: "") + } + var requestBody: RequestBodyRepresentable? { nil } var security: [SecurityRequirementRepresentable]? { nil } diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/DateTimeSchema.swift b/Sources/FeatherOpenAPIKit/DateTimeSchema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/DateTimeSchema.swift rename to Sources/FeatherOpenAPIKit/DateTimeSchema.swift diff --git a/Sources/FeatherOpenAPIKit/Extensions/JSONSchema+Enumeration.swift b/Sources/FeatherOpenAPIKit/Extensions/JSONSchema+Enumeration.swift deleted file mode 100644 index b1753fb..0000000 --- a/Sources/FeatherOpenAPIKit/Extensions/JSONSchema+Enumeration.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 10/01/2024. -// - -import OpenAPIKit30 - -public extension JSONSchema { - - static func enumeration( - description: String?, - allowedValues: [AnyCodable], - defaultValue: AnyCodable? = nil, - example: AnyCodable? = nil - ) -> JSONSchema { - .string( - format: .generic, - description: description, - allowedValues: allowedValues, - defaultValue: defaultValue, - example: example - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Extensions/JSONSchema+Text.swift b/Sources/FeatherOpenAPIKit/Extensions/JSONSchema+Text.swift deleted file mode 100644 index 0a63682..0000000 --- a/Sources/FeatherOpenAPIKit/Extensions/JSONSchema+Text.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - -import OpenAPIKit30 - -public extension JSONSchema { - - static func text( - description: String?, - example: AnyCodable? = nil - ) -> Self { - .string( - format: .generic, - description: description, - example: example - ) - } - -} diff --git a/Sources/FeatherOpenAPIKit/Extensions/String+LowercasedFirstLetter.swift b/Sources/FeatherOpenAPIKit/Extensions/String+LowercasedFirstLetter.swift deleted file mode 100644 index 121e2a8..0000000 --- a/Sources/FeatherOpenAPIKit/Extensions/String+LowercasedFirstLetter.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - -extension String { - - func lowercasedFirstLetter() -> String { - guard !isEmpty else { - return self - } - return prefix(1).lowercased() + dropFirst() - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Component.swift b/Sources/FeatherOpenAPIKit/Interfaces/Component.swift deleted file mode 100644 index cb86666..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Component.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - -public protocol Component { - - // NOTE: no support for examples yet in OpeAPIKit - // static var examples: [Example.Type] { get } - static var schemas: [Schema.Type] { get } - - static var parameters: [Parameter.Type] { get } - static var headers: [Header.Type] { get } - static var requestBodies: [RequestBody.Type] { get } - static var securitySchemes: [SecurityScheme.Type] { get } - - static var responses: [Response.Type] { get } - - static var tags: [Tag.Type] { get } - static var operations: [Operation.Type] { get } - static var pathItems: [PathItem.Type] { get } - - static func getComponentsOfType() -> [T] - static func getComponentsOfType(_: T.Type) -> [T] -} - -public extension Component { - static var schemas: [Schema.Type] { getComponentsOfType() } - static var parameters: [Parameter.Type] { getComponentsOfType() } - static var headers: [Header.Type] { getComponentsOfType() } - static var requestBodies: [RequestBody.Type] { getComponentsOfType() } - static var securitySchemes: [SecurityScheme.Type] { getComponentsOfType() } - static var responses: [Response.Type] { getComponentsOfType() } - static var tags: [Tag.Type] { getComponentsOfType() } - static var operations: [Operation.Type] { getComponentsOfType() } - static var pathItems: [PathItem.Type] { getComponentsOfType() } - - static func getComponentsOfType(_: T.Type) -> [T] { - getComponentsOfType() - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Document.swift b/Sources/FeatherOpenAPIKit/Interfaces/Document.swift deleted file mode 100644 index 5f213b7..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Document.swift +++ /dev/null @@ -1,208 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - -import OpenAPIKit30 -import OpenAPIKitCore - -// https://spec.openapis.org/oas/latest.html -public protocol Document { - var components: [Component.Type] { get } - - func openAPIDocument() throws -> OpenAPI.Document - - func schemas() throws -> OpenAPI.ComponentDictionary - func parameters() throws - -> OpenAPI.ComponentDictionary - func headers() throws - -> OpenAPI.ComponentDictionary - func requestBodies() throws - -> OpenAPI.ComponentDictionary - func securitySchemes() throws - -> OpenAPI.ComponentDictionary - func responses() throws - -> OpenAPI.ComponentDictionary - func tags() throws -> [OpenAPI.Tag] - func paths() throws -> OpenAPI.PathItem.Map - - func composedDocument( - info: OpenAPI.Document.Info, - servers: [OpenAPI.Server] - ) throws -> OpenAPI.Document -} - -public struct ComposeDocumentError: Swift.Error { - public let message: String -} - -public extension Document { - - static private func filterIdentifiables( - lists: [[T]] - ) throws -> [T] { - var ret: [String: T] = [:] - - for list in lists { - for item in list { - let itemId = item as! any Identifiable.Type - - if ret[itemId.id] != nil { - if itemId.override == false { - throw ComposeDocumentError.init( - message: - "Feather OpenAPI item id is duplicated: '\(itemId.id)' (Did you forget to include override=true?)" - ) - } - } - else { - if itemId.override { - throw ComposeDocumentError.init( - message: - "Feather OpenAPI item '\(itemId.id)' is set as override but has no parent. (Are the component orders correct? Or are the IDs the same?)" - ) - } - } - - ret[itemId.id] = item - } - } - - return ret.sorted { $0.key < $1.key }.map { $0.value } - } - - func schemas() throws -> OpenAPI.ComponentDictionary { - return - try Self.filterIdentifiables( - lists: components.map { - $0.schemas - } - ) - .reduce(into: [:]) { into, item in - into[item.componentKey] = item.openAPISchema() - } - } - - func parameters() throws - -> OpenAPI.ComponentDictionary - { - return - try Self.filterIdentifiables( - lists: components.map { - $0.parameters - } - ) - .reduce(into: [:]) { into, item in - into[item.componentKey] = .init(item.openAPIParameter()) - } - } - - func headers() throws - -> OpenAPI.ComponentDictionary - { - return - try Self.filterIdentifiables( - lists: components.map { - $0.headers - } - ) - .reduce(into: [:]) { into, item in - into[item.componentKey] = .init(item.openAPIHeader()) - } - } - - func requestBodies() throws - -> OpenAPI.ComponentDictionary - { - return - try Self.filterIdentifiables( - lists: components.map { - $0.requestBodies - } - ) - .reduce(into: [:]) { into, item in - into[item.componentKey] = .init(item.openAPIRequestBody()) - } - } - - func securitySchemes() throws - -> OpenAPI.ComponentDictionary - { - return - try Self.filterIdentifiables( - lists: components.map { - $0.securitySchemes - } - ) - .reduce(into: [:]) { into, item in - into[item.componentKey] = .init(item.openAPISecurityScheme()) - } - } - - func responses() throws - -> OpenAPI.ComponentDictionary - { - - return - try Self.filterIdentifiables( - lists: components.map { - $0.responses - } - ) - .reduce(into: [:]) { into, item in - into[item.componentKey] = .init(item.openAPIResponse()) - } - } - - // MARK: - - - func tags() throws -> [OpenAPI.Tag] { - return - try Self.filterIdentifiables( - lists: components.map { - $0.tags - } - ) - .reduce(into: []) { into, item in - into.append(item.openAPITag()) - } - .sorted { lhs, rhs in - lhs.name < rhs.name - } - } - - func paths() throws -> OpenAPI.PathItem.Map { - return - try Self.filterIdentifiables( - lists: components.map { - $0.pathItems - } - ) - .sorted { $0.path.value < $1.path.value } - .reduce(into: [:]) { into, item in - into[item.openAPIPath] = .init(item.openAPIPathItem()) - } - } - - func composedDocument( - info: OpenAPI.Document.Info, - servers: [OpenAPI.Server] - ) throws -> OpenAPI.Document { - .init( - info: info, - servers: servers, - paths: try paths(), - components: .init( - schemas: try schemas(), - responses: try responses(), - parameters: try parameters(), - requestBodies: try requestBodies(), - headers: try headers(), - securitySchemes: try securitySchemes() - ), - tags: try tags() - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Header/Header.swift b/Sources/FeatherOpenAPIKit/Interfaces/Header/Header.swift deleted file mode 100644 index 88038c2..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Header/Header.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - -import OpenAPIKit30 - -public protocol OpenAPIHeader: Identifiable { - static func openAPIHeader() -> OpenAPI.Header -} - -public protocol Header: OpenAPIHeader { - static var name: String { get } - static var description: String { get } - static var schema: Schema.Type { get } -} - -public extension Header { - - static var id: String { name } - - static func openAPIHeader() -> OpenAPI.Header { - .init( - schema: schema.reference(), - description: description - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Identifiable.swift b/Sources/FeatherOpenAPIKit/Interfaces/Identifiable.swift deleted file mode 100644 index 5e178f3..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Identifiable.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - -import OpenAPIKit30 - -public protocol Identifiable: Sendable { - static var id: String { get } - static var override: Bool { get } -} - -public extension Identifiable { - - static var id: String { - var components = String(reflecting: self).split(separator: ".") - components.remove(at: 0) // remove namespace - components.remove(at: 2) // remove enum name - return - components - .joined(separator: "") - .replacing("GenericComponent", with: "Generic") - } - - static var override: Bool { false } -} - -public extension Identifiable { - static var componentKey: OpenAPI.ComponentKey { .init(stringLiteral: id) } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Operation/Operation.swift b/Sources/FeatherOpenAPIKit/Interfaces/Operation/Operation.swift deleted file mode 100644 index ae76bd5..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Operation/Operation.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - -import OpenAPIKit30 - -public protocol OpenAPIOperation: Identifiable { - static func openAPIOperation() -> OpenAPI.Operation -} - -public protocol Operation: OpenAPIOperation { - static var operationId: String { get } - static var tag: Tag.Type { get } - static var summary: String { get } - static var description: String { get } - - static var parameters: [Parameter.Type] { get } - static var requestBody: RequestBody.Type? { get } - static var responses: [OperationResponse] { get } - static var security: [SecurityScheme.Type] { get } -} - -public extension Operation { - - static var operationId: String { - var components = String(reflecting: self) - .split(separator: ".") - .dropFirst() - .map(String.init) - - components.remove(at: 2) - if let last = components.popLast()?.lowercasedFirstLetter() { - components.insert(last, at: 0) - } - return components.joined(separator: "") - } - - static var parameters: [Parameter.Type] { [] } - static var requestBody: RequestBody.Type? { nil } - static var security: [SecurityScheme.Type] { [] } - - static func openAPIOperation() -> OpenAPI.Operation { - .init( - tags: tag.name, - summary: summary, - description: description, - operationId: operationId, - parameters: parameters.map { .reference(.component(named: $0.id)) }, - requestBody: requestBody?.openAPIRequestBody(), - responses: responses.reduce(into: [:]) { - $0[.init(integerLiteral: $1.statusCode)] = .reference( - .component(named: $1.response.id) - ) - }, - security: security.map { [$0.reference(): []] } - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Operation/OperationResponse.swift b/Sources/FeatherOpenAPIKit/Interfaces/Operation/OperationResponse.swift deleted file mode 100644 index b196c5c..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Operation/OperationResponse.swift +++ /dev/null @@ -1,79 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - -public struct OperationResponse { - - public let statusCode: Int - public let response: Response.Type - - public init( - _ statusCode: Int, - _ response: Response.Type - ) { - self.statusCode = statusCode - self.response = response - } -} - -public extension OperationResponse { - - static func ok(_ response: Response.Type) -> Self { - .init(200, response) - } - - static func found(_ response: Response.Type) -> Self { - .init(302, response) - } - - static func seeOther(_ response: Response.Type) -> Self { - .init(303, response) - } - - static func temporaryRedirect(_ response: Response.Type) -> Self { - .init(307, response) - } - - static func badRequest(_ response: Response.Type) -> Self { - .init(400, response) - } - - static func unauthorized(_ response: Response.Type) -> Self { - .init(401, response) - } - - static func forbidden(_ response: Response.Type) -> Self { - .init(403, response) - } - - static func notFound(_ response: Response.Type) -> Self { - .init(404, response) - } - - static func methodNotAllowed(_ response: Response.Type) -> Self { - .init(405, response) - } - - static func notAcceptable(_ response: Response.Type) -> Self { - .init(406, response) - } - - static func conflict(_ response: Response.Type) -> Self { - .init(409, response) - } - - static func gone(_ response: Response.Type) -> Self { - .init(410, response) - } - - static func unsupportedMediaType(_ response: Response.Type) -> Self { - .init(415, response) - } - - static func unprocessableContent(_ response: Response.Type) -> Self { - .init(422, response) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Parameter/HeaderParameter.swift b/Sources/FeatherOpenAPIKit/Interfaces/Parameter/HeaderParameter.swift deleted file mode 100644 index 42ed81a..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Parameter/HeaderParameter.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 15/02/2024. -// - -import OpenAPIKit30 - -public protocol HeaderParameter: Parameter {} - -public extension HeaderParameter { - - // TODO: - static var context: OpenAPI.Parameter.Context { - .header(required: Self.required) - // .header( - // required: Self.required, - // schemaOrContent: .schema( - // .schema( - // Self.schema.openAPISchema(), - // style: .default( - // for: .header - // ) - // ) - // ) - // ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Parameter/Parameter.swift b/Sources/FeatherOpenAPIKit/Interfaces/Parameter/Parameter.swift deleted file mode 100644 index 50302eb..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Parameter/Parameter.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - -import OpenAPIKit30 - -public protocol OpenAPIParameter: Identifiable { - static func openAPIParameter() -> OpenAPI.Parameter -} - -public protocol Parameter: OpenAPIParameter { - static var name: String { get } - static var context: OpenAPI.Parameter.Context { get } - static var schema: Schema.Type { get } - static var description: String { get } - static var required: Bool { get } -} - -public extension Parameter { - - static var required: Bool { true } - - static var path: Path { .parameter(name) } - - static func openAPIParameter() -> OpenAPI.Parameter { - .init( - name: name, - context: context, - schema: schema.reference(), - description: description - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Parameter/PathParameter.swift b/Sources/FeatherOpenAPIKit/Interfaces/Parameter/PathParameter.swift deleted file mode 100644 index 8dc86bb..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Parameter/PathParameter.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - -import OpenAPIKit30 - -public protocol PathParameter: Parameter {} - -public extension PathParameter { - - // TODO: - static var context: OpenAPI.Parameter.Context { - .path - // .path( - // schema: Self.schema.openAPISchema() - // ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Parameter/QueryParameter.swift b/Sources/FeatherOpenAPIKit/Interfaces/Parameter/QueryParameter.swift deleted file mode 100644 index 87c6f94..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Parameter/QueryParameter.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - -import OpenAPIKit30 - -public protocol QueryParameter: Parameter {} - -public extension QueryParameter { - - static var required: Bool { false } - - // TODO: - static var context: OpenAPI.Parameter.Context { - .query(required: Self.required) - // .query( - // required: Self.required, - // schemaOrContent: .schema( - // .schema( - // Self.schema.openAPISchema(), - // style: .default( - // for: .query - // ) - // ) - // ) - // ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/PathItem/PathItem.swift b/Sources/FeatherOpenAPIKit/Interfaces/PathItem/PathItem.swift deleted file mode 100644 index d516a58..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/PathItem/PathItem.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - -import OpenAPIKit30 - -public protocol OpenAPIPathItem: Identifiable { - static var openAPIPath: OpenAPI.Path { get } - static func openAPIPathItem() -> OpenAPI.PathItem -} - -public protocol PathItem: OpenAPIPathItem { - - static var path: Path { get } - - static var summary: String? { get } - static var description: String? { get } - static var parameters: [Parameter.Type] { get } - static var get: Operation.Type? { get } - static var put: Operation.Type? { get } - static var post: Operation.Type? { get } - static var delete: Operation.Type? { get } - static var options: Operation.Type? { get } - static var head: Operation.Type? { get } - static var patch: Operation.Type? { get } - static var trace: Operation.Type? { get } -} - -public extension PathItem { - - static var openAPIPath: OpenAPI.Path { - .init(stringLiteral: path.value) - } - - static var summary: String? { nil } - static var description: String? { nil } - static var parameters: [Parameter.Type] { [] } - static var get: Operation.Type? { nil } - static var put: Operation.Type? { nil } - static var post: Operation.Type? { nil } - static var delete: Operation.Type? { nil } - static var options: Operation.Type? { nil } - static var head: Operation.Type? { nil } - static var patch: Operation.Type? { nil } - static var trace: Operation.Type? { nil } - - static func openAPIPathItem() -> OpenAPI.PathItem { - .init( - summary: summary, - description: description, - servers: nil, - parameters: parameters.map { .reference(.component(named: $0.id)) }, - get: get?.openAPIOperation(), - put: put?.openAPIOperation(), - post: post?.openAPIOperation(), - delete: delete?.openAPIOperation(), - options: options?.openAPIOperation(), - head: head?.openAPIOperation(), - patch: patch?.openAPIOperation(), - trace: trace?.openAPIOperation(), - vendorExtensions: [:] - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/BinaryBody.swift b/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/BinaryBody.swift deleted file mode 100644 index 61ccec8..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/BinaryBody.swift +++ /dev/null @@ -1,28 +0,0 @@ -import OpenAPIKit30 - -public protocol BinaryBody: RequestBody { - static var contentType: OpenAPI.ContentType { get } - static var description: String { get } - static var required: Bool { get } -} - -extension BinaryBody { - - public static var required: Bool { true } - - public static func openAPIRequestBody() -> OpenAPI.Request { - .init( - description: description, - content: [ - contentType: .init( - schema: .string( - format: .binary, - // contentMediaType: .other("application/octet-stream") - ) - ) - ], - required: required - ) - } -} - diff --git a/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/FormBody.swift b/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/FormBody.swift deleted file mode 100644 index 1f5a3b0..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/FormBody.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// File.swift -// -// Created by gerp83 on 28/08/2024 -// - -import OpenAPIKit30 - -public protocol FormBody: RequestBody { - static var description: String { get } - static var schema: Schema.Type { get } - static var required: Bool { get } -} - -public extension FormBody { - - static var required: Bool { true } - - static func openAPIRequestBody() -> OpenAPI.Request { - .init( - description: description, - content: [ - .form: .init( - schemaReference: .component( - named: schema.id - ) - ) - ], - required: required, - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/JSONBody.swift b/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/JSONBody.swift deleted file mode 100644 index 3bc256d..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/JSONBody.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - -import OpenAPIKit30 - -public protocol JSONBody: RequestBody { - static var description: String { get } - static var schema: Schema.Type { get } - static var required: Bool { get } -} - -public extension JSONBody { - - static var required: Bool { true } - - static func openAPIRequestBody() -> OpenAPI.Request { - .init( - description: description, - content: [ - .json: .init( - schemaReference: .component( - named: schema.id - ) - ) - ], - required: required - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/RequestBody.swift b/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/RequestBody.swift deleted file mode 100644 index 2932c89..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/RequestBody/RequestBody.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - -import OpenAPIKit30 - -public protocol OpenAPIRequestBody: Identifiable { - static func openAPIRequestBody() -> OpenAPI.Request -} - -public protocol RequestBody: OpenAPIRequestBody { - -} - -extension RequestBody {} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Response/BinaryResponse.swift b/Sources/FeatherOpenAPIKit/Interfaces/Response/BinaryResponse.swift deleted file mode 100644 index f9ce780..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Response/BinaryResponse.swift +++ /dev/null @@ -1,22 +0,0 @@ -import OpenAPIKit30 - -public protocol BinaryResponse: Response {} - -public extension BinaryResponse { - - static func openAPIResponse() -> OpenAPI.Response { - .init( - description: description, - headers: openAPIHeaderMap(), - content: openAPIContentMap() + [ - .any: .init( - schema: .string( - format: .binary - // contentMediaType: .other("application/octet-stream") - ) - ) - ] - - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Response/JSONResponse.swift b/Sources/FeatherOpenAPIKit/Interfaces/Response/JSONResponse.swift deleted file mode 100644 index df4ca4b..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Response/JSONResponse.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - -import OpenAPIKit30 - -public protocol JSONResponse: Response { - - static var schema: Schema.Type { get } -} - -public extension JSONResponse { - - static var contents: [OpenAPI.ContentType: Schema.Type] { - [ - .json: schema - ] - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Response/Response.swift b/Sources/FeatherOpenAPIKit/Interfaces/Response/Response.swift deleted file mode 100644 index f74b944..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Response/Response.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - -import OpenAPIKit30 - -public protocol OpenAPIResponse: Identifiable { - static func openAPIResponse() -> OpenAPI.Response -} - -public protocol Response: OpenAPIResponse { - static var description: String { get } - static var headers: [Header.Type] { get } - static var contents: [OpenAPI.ContentType: Schema.Type] { get } -} - -public extension Response { - - static var headers: [Header.Type] { [] } - - static var contents: [OpenAPI.ContentType: Schema.Type] { - [:] - } - - static func openAPIResponse() -> OpenAPI.Response { - .init( - description: description, - headers: openAPIHeaderMap(), - content: openAPIContentMap() - ) - } - - static func openAPIContentMap() -> OpenAPI.Content.Map { - var result: OpenAPI.Content.Map = [:] - for (key, content) in contents { - result[key] = .init(content.reference()) - } - return result - } - - static func openAPIHeaderMap() -> OpenAPI.Header.Map? { - guard !headers.isEmpty else { - return nil - } - var result: OpenAPI.Header.Map = [:] - for header in headers { - result[header.id] = .reference(.component(named: header.id)) - } - return result - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/ArraySchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/ArraySchema.swift deleted file mode 100644 index 70f033f..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Schema/ArraySchema.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - -import OpenAPIKit30 - -public protocol ArraySchema: Schema { - static var minItems: Int? { get } - static var maxItems: Int? { get } - static var items: Schema.Type { get } -} - -public extension ArraySchema { - static var minItems: Int? { 0 } - static var maxItems: Int? { 1000 } -} - -public extension ArraySchema { - - static func openAPISchema() -> JSONSchema { - .array( - description: description, - minItems: minItems, - maxItems: maxItems, - items: items.reference() - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/BooleanSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/BooleanSchema.swift deleted file mode 100644 index de68b99..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Schema/BooleanSchema.swift +++ /dev/null @@ -1,18 +0,0 @@ -import OpenAPIKit30 - -public protocol BooleanSchema: Schema { - static var defaultValue: Bool? { get } -} - -public extension BooleanSchema { - static var defaultValue: Bool? { nil } -} - -extension BooleanSchema { - public static func openAPISchema() -> JSONSchema { - .boolean( - description: description, - defaultValue: defaultValue.map { .init($0) } - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/DoubleSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/DoubleSchema.swift deleted file mode 100644 index e21547e..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Schema/DoubleSchema.swift +++ /dev/null @@ -1,20 +0,0 @@ -import OpenAPIKit30 - -public protocol DoubleSchema: NumberSchema { - associatedtype T = Double -} - -extension DoubleSchema where T == Double { - - public static func openAPISchema() -> JSONSchema { - .number( - format: .double, - required: true, - description: description, - maximum: maximum, - minimum: minimum, - defaultValue: defaultValue.map { .init($0) }, - example: example.map { .init($0) } - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/EmailSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/EmailSchema.swift deleted file mode 100644 index df3d2d0..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Schema/EmailSchema.swift +++ /dev/null @@ -1,16 +0,0 @@ -import OpenAPIKit30 - -public protocol EmailSchema: Schema { - static var example: String? { get } -} - -extension EmailSchema { - public static func openAPISchema() -> JSONSchema { - .string( - format: .generic, - required: true, - description: description, - example: example.map { .init($0) } - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/EnumSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/EnumSchema.swift deleted file mode 100644 index 1559759..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Schema/EnumSchema.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - -import OpenAPIKit30 - -public protocol EnumSchema: Schema { - static var allowedValues: [String] { get } - static var defaultValue: String? { get } - static var example: String? { get } -} - -public extension EnumSchema { - static var defaultValue: String? { nil } - static var example: String? { nil } -} - -public extension EnumSchema { - - static func openAPISchema() -> JSONSchema { - var anyDefault: AnyCodable? = nil - if let defaultValue { - anyDefault = .init(stringLiteral: defaultValue) - } - return .enumeration( - description: description, - allowedValues: allowedValues.map { .init(stringLiteral: $0) }, - defaultValue: anyDefault, - example: example.map { .init(stringLiteral: $0) } - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/FloatSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/FloatSchema.swift deleted file mode 100644 index 929bd49..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Schema/FloatSchema.swift +++ /dev/null @@ -1,20 +0,0 @@ -import OpenAPIKit30 - -public protocol FloatSchema: NumberSchema { - associatedtype T = Float -} - -extension FloatSchema where T == Float { - - public static func openAPISchema() -> JSONSchema { - .number( - format: .float, - required: true, - description: description, - maximum: maximum.map { (Double($0.0), exclusive: $0.1) }, - minimum: minimum.map { (Double($0.0), exclusive: $0.1) }, - defaultValue: defaultValue.map { .init($0) }, - example: example.map { .init($0) } - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/IDSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/IDSchema.swift deleted file mode 100644 index ec6521e..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Schema/IDSchema.swift +++ /dev/null @@ -1,16 +0,0 @@ -public protocol IDSchema: TextSchema { - -} - -extension IDSchema { - - public static var examples: [String] { - [ - "P6p5WCctPKqhYKtGMUoTQ" - // "9lRYd11kppK1Nd6M0QMJh", - // "iIPAqgGFZRkyYmjU76YfG", - // "Mgzt9YAQUpjzNxYkf_xz2", - // "slIgHPBDHvXa4D6NQJ2on", - ] - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/Int32Schema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/Int32Schema.swift deleted file mode 100644 index 7f0f75d..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Schema/Int32Schema.swift +++ /dev/null @@ -1,23 +0,0 @@ -import OpenAPIKit30 - -public protocol Int32Schema: NumberSchema { - associatedtype T = Int32 -} - -extension Int32Schema where T == Int32 { - - public static func openAPISchema() -> JSONSchema { - .integer( - format: .int32, - required: true, - description: description, - - //TODO: add Int64 cast after openapi kit fixed - maximum: maximum.map { (Int($0.0), exclusive: $0.exclusive) }, - minimum: maximum.map { (Int($0.0), exclusive: $0.exclusive) }, - - defaultValue: defaultValue.map { .init($0) }, - example: example.map { .init($0) } - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/Int64Schema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/Int64Schema.swift deleted file mode 100644 index 0b9e0d6..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Schema/Int64Schema.swift +++ /dev/null @@ -1,22 +0,0 @@ -import OpenAPIKit30 - -public protocol Int64Schema: NumberSchema { - //TODO: use int64, remove Int cast after openapi kit fixed - associatedtype T = Int -} - -//TODO: use int64, remove Int cast after openapi kit fixed -extension Int64Schema where T == Int { - - public static func openAPISchema() -> JSONSchema { - .integer( - format: .int64, - required: true, - description: description, - maximum: maximum, - minimum: minimum, - defaultValue: defaultValue.map { .init($0) }, - example: example.map { .init($0) } - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/IntSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/IntSchema.swift deleted file mode 100644 index 8f42608..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Schema/IntSchema.swift +++ /dev/null @@ -1,20 +0,0 @@ -import OpenAPIKit30 - -public protocol IntSchema: NumberSchema { - associatedtype T = Int -} - -extension IntSchema where T == Int { - - public static func openAPISchema() -> JSONSchema { - .integer( - format: .unspecified, - required: true, - description: description, - maximum: maximum, - minimum: minimum, - defaultValue: defaultValue.map { .init(integerLiteral: $0) }, - example: example.map { .init(integerLiteral: $0) } - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/NanoIDSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/NanoIDSchema.swift deleted file mode 100644 index 2d63bfc..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Schema/NanoIDSchema.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 16/03/2024. -// - -import OpenAPIKit30 - -public protocol NanoIDSchema: Schema { - static var example: String? { get } -} - -public extension NanoIDSchema { - - static var example: String? { - "xHVX15b8z_wQDPH93uVp5" - } - - static func openAPISchema() -> JSONSchema { - .string( - format: .generic, - description: description, - example: example.map { .init($0) } - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/ObjectSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/ObjectSchema.swift deleted file mode 100644 index 381c5ce..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Schema/ObjectSchema.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - -import OpenAPIKit30 - -public protocol ObjectSchema: Schema { - static var properties: [ObjectSchemaProperty] { get } -} - -public extension ObjectSchema { - - static func openAPISchema() -> JSONSchema { - .object( - description: description, - properties: properties.reduce(into: [:]) { - $0[$1.name] = $1.schema.reference(required: $1.required) - } - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/ObjectSchemaProperty.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/ObjectSchemaProperty.swift deleted file mode 100644 index 7e83742..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Schema/ObjectSchemaProperty.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - -import OpenAPIKit30 - -public struct ObjectSchemaProperty { - - public let name: String - public let schema: Schema.Type - public let required: Bool - - public init( - _ name: String, - _ schema: Schema.Type, - required: Bool = true - ) { - self.name = name - self.schema = schema - self.required = required - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/PasswordSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/PasswordSchema.swift deleted file mode 100644 index 1567f88..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Schema/PasswordSchema.swift +++ /dev/null @@ -1,16 +0,0 @@ -import OpenAPIKit30 - -public protocol PasswordSchema: Schema { - static var example: String? { get } -} - -extension PasswordSchema { - public static func openAPISchema() -> JSONSchema { - .string( - format: .password, - required: true, - description: description, - example: example.map { .init($0) } - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/Schema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/Schema.swift deleted file mode 100644 index 79ab437..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Schema/Schema.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - -import OpenAPIKit30 - -public protocol OpenAPISchema: Identifiable { - static func openAPISchema() -> JSONSchema -} - -public extension OpenAPISchema { - - static func reference(required: Bool = true) -> JSONSchema { - .reference(.component(named: id), required: required) - } - - static func reference() -> OpenAPI.Content { - .init(schemaReference: .component(named: id)) - } -} - -public protocol Schema: OpenAPISchema { - static var description: String? { get } -} - -extension Schema { - - public static var description: String? { nil } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/TextSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/TextSchema.swift deleted file mode 100644 index 3d129aa..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Schema/TextSchema.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - -import OpenAPIKit30 - -public protocol TextSchema: Schema { - static var example: String? { get } -} - -public extension TextSchema { - static var example: String? { nil } -} - -public extension TextSchema { - - static func openAPISchema() -> JSONSchema { - .text( - description: description, - example: example.map { .init(stringLiteral: $0) } - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/UUIDSchema.swift b/Sources/FeatherOpenAPIKit/Interfaces/Schema/UUIDSchema.swift deleted file mode 100644 index 83f4d13..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Schema/UUIDSchema.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - -import OpenAPIKit30 -import Foundation - -public protocol UUIDSchema: Schema { - static var example: UUID? { get } -} - -public extension UUIDSchema { - - static var example: UUID? { - .init(uuidString: "F257448D-73F6-4D6F-BB8A-8D756A622F70")! - } - - static func openAPISchema() -> JSONSchema { - .string( - format: .generic, - description: description, - example: example.map { .init($0.uuidString) } - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/SecurityScheme/SecurityScheme.swift b/Sources/FeatherOpenAPIKit/Interfaces/SecurityScheme/SecurityScheme.swift deleted file mode 100644 index fe5f24f..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/SecurityScheme/SecurityScheme.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - -import OpenAPIKit30 - -public protocol OpenAPISecurityScheme: Identifiable { - static func openAPISecurityScheme() -> OpenAPI.SecurityScheme -} - -public extension OpenAPISecurityScheme { - - static func securityRequirement() -> [OpenAPI.SecurityRequirement] { - [ - [ - reference(): [] - ] - ] - } - - static func reference() -> JSONReference { - .component(named: id) - } -} - -public protocol SecurityScheme: OpenAPISecurityScheme { - -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Tag/Tag.swift b/Sources/FeatherOpenAPIKit/Interfaces/Tag/Tag.swift deleted file mode 100644 index 1b62dc7..0000000 --- a/Sources/FeatherOpenAPIKit/Interfaces/Tag/Tag.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - -import OpenAPIKit30 - -public protocol OpenAPITag: Identifiable { - static func openAPITag() -> OpenAPI.Tag -} - -public protocol Tag: OpenAPITag { - static var name: String { get } - static var description: String { get } -} - -public extension Tag { - - static var name: String { id } - static var description: String { "" } - - static func openAPITag() -> OpenAPI.Tag { - .init(name: name, description: description) - } -} diff --git a/Sources/FeatherOpenAPIKit/Interfaces/Schema/NumberSchema.swift b/Sources/FeatherOpenAPIKit/NumberSchema.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/Schema/NumberSchema.swift rename to Sources/FeatherOpenAPIKit/NumberSchema.swift diff --git a/Sources/FeatherOpenAPIKit/Interfaces/PathItem/Path.swift b/Sources/FeatherOpenAPIKit/Path.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Interfaces/PathItem/Path.swift rename to Sources/FeatherOpenAPIKit/Path.swift diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Headers.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Headers.swift index 8e4ea3a..4dd7fbe 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Headers.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Headers.swift @@ -5,22 +5,14 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension Example.Model { - static var headers: [Header.Type] { - [ - Headers.CustomResponseHeader.self - ] - } - - enum Headers { - - enum CustomResponseHeader: Header { - static let name = "X-Custom-Response-Header" - static let description: String = "My custom response header" - static var schema: Schema.Type { Schemas.CustomHeader.self } + struct CustomResponseHeader: HeaderRepresentable { + var description: String? { "My custom response header" } + var schema: any OpenAPISchemaRepresentable { + CustomHeaderSchema().reference() } } } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Operations.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Operations.swift index 0197095..10bc6ff 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Operations.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Operations.swift @@ -5,55 +5,37 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension Example.Model { - static var operations: [Operation.Type] { - [ - Operations.Get.self, - Operations.Create.self, - ] + struct GetOperation: OperationRepresentable { + var tags: [TagRepresentable] { [ModelTag()] } + var summary: String? { "Detail example" } + var description: String? { "Detail example detail" } + var parameters: [ParameterRepresentable] { + [ + CustomRequestHeaderParameter().reference() + ] + } + var responseMap: ResponseMap { + [ + 200: DetailResponse().reference() + ] + } } - enum Operations { - - enum Get: Operation { - static var tag: Tag.Type { Tags.Main.self } - static let summary = "Detail example" - static let description = "Detail example detail" - static var parameters: [Parameter.Type] { - [ - // Parameters.Id.self, - Parameters.CustomRequestHeader.self - ] - } - static var responses: [OperationResponse] { - [ - .init(200, Responses.Detail.self) - ] - } + struct CreateOperation: OperationRepresentable { + var tags: [TagRepresentable] { [ModelTag()] } + var summary: String? { "Create example" } + var description: String? { "Create example detail" } + var requestBody: RequestBodyRepresentable? { + CreateRequestBody().reference() } - - enum Create: Operation { - - static var tag: Tag.Type { Tags.Main.self } - static var summary: String { "Create example" } - static var description: String { "Create example detail" } - static let requestBody: RequestBody.Type? = RequestBodies.Create - .self - - static var responses: [OperationResponse] { - [ - .init(200, Responses.Detail.self) - ] - } - static var security: [SecurityScheme.Type] { - [ - SecuritySchemes.BearerToken.self - ] - } + var responseMap: ResponseMap { + [ + 200: DetailResponse().reference() + ] } - } } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Parameters.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Parameters.swift index 9d84953..bc56e03 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Parameters.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Parameters.swift @@ -5,29 +5,23 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension Example.Model { - static var parameters: [Parameter.Type] { - [ - Parameters.Id.self, - Parameters.CustomRequestHeader.self, - ] - } - - enum Parameters { - - enum Id: PathParameter { - static let name = "id" - static let description = "Example parameter" - static var schema: Schema.Type { Schemas.Id.self } + struct IdParameter: PathParameterRepresentable { + var name: String { "id" } + var description: String? { "Example parameter" } + var schema: any OpenAPISchemaRepresentable { + IdSchema().reference() } + } - enum CustomRequestHeader: HeaderParameter { - static let name = "CustomRequestHeader" - static let description = "Example request header parameter" - static var schema: Schema.Type { Schemas.CustomHeader.self } + struct CustomRequestHeaderParameter: HeaderParameterRepresentable { + var name: String { "CustomRequestHeader" } + var description: String? { "Example request header parameter" } + var schema: any OpenAPISchemaRepresentable { + CustomHeaderSchema().reference() } } } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+PathItems.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+PathItems.swift index a4db94e..288f079 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+PathItems.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+PathItems.swift @@ -5,35 +5,15 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension Example.Model { - static var pathItems: [PathItem.Type] { - [ - PathItems.Main.self, - PathItems.Identified.self, - ] + struct MainPathItem: PathItemRepresentable { + var post: OperationRepresentable? { CreateOperation() } } - enum PathItems { - - enum Main: PathItem { - static var path: Path { "/example/models" } - - static var post: Operation.Type? { Operations.Create.self } - } - - enum Identified: PathItem { - static var path: Path { Main.path / Parameters.Id.path } - static var parameters: [Parameter.Type] { - [ - Parameters.Id.self - ] - } - - static var get: Operation.Type? { Operations.Get.self } - } - + struct IdentifiedPathItem: PathItemRepresentable { + var get: OperationRepresentable? { GetOperation() } } } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift index 52687e0..b8c4c44 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift @@ -5,21 +5,12 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension Example.Model { - static var requestBodies: [RequestBody.Type] { - [ - RequestBodies.Create.self - ] - } - - enum RequestBodies { - - enum Create: JSONBody { - static let description = "Create example" - static var schema: Schema.Type { Schemas.Create.self } - } + struct CreateRequestBody: JSONRequestBodyRepresentable { + var description: String? { "Create example" } + var schema: CreateSchema { CreateSchema() } } } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Responses.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Responses.swift index a65ecca..7ca5675 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Responses.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Responses.swift @@ -5,36 +5,27 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI import OpenAPIKit30 extension Example.Model { - static var responses: [Response.Type] { - [ - Responses.Detail.self - ] - } - - enum Responses { - - enum Custom: Response { - static let description = "Example" - static var contents: [OpenAPI.ContentType: Schema.Type] { - [ - .xml: Schemas.Detail.self - ] - } + struct CustomResponse: ResponseRepresentable { + var description: String { "Example" } + var contentMap: ContentMap { + [ + .xml: Content(DetailSchema()) + ] } + } - enum Detail: JSONResponse { - static let description = "Example" - static var headers: [Header.Type] { - [ - Headers.CustomResponseHeader.self - ] - } - static var schema: Schema.Type { Schemas.Detail.self } + struct DetailResponse: JSONResponseRepresentable { + var description: String { "Example" } + var schema: DetailSchema { DetailSchema() } + var headerMap: HeaderMap { + [ + "X-Custom-Response-Header": CustomResponseHeader().reference() + ] } } } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Schemas.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Schemas.swift index 6c2e7e2..1bfb611 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Schemas.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Schemas.swift @@ -5,98 +5,74 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI +import OpenAPIKit30 extension Example.Model { - static var schemas: [Schema.Type] { - [ - Schemas.Id.self, - Schemas.Key.self, - Schemas.Create.self, - Schemas.Patch.self, - Schemas.Detail.self, - Schemas.List.self, - Schemas.List.Item.self, - Schemas.CustomHeader.self, - Schemas.PatchOverride.self, - ] + struct IdSchema: StringSchemaRepresentable { + var description: String? { "Unique example model identifier" } } - enum Schemas { - - enum Id: UUIDSchema { - static let description = "Unique example model identifier" - } - - enum CustomHeader: TextSchema { - static let description = "Custom header" - static let example: String? = "my-example-key" - } + struct CustomHeaderSchema: StringSchemaRepresentable { + var description: String? { "Custom header" } + var example: String? { "my-example-key" } + } - enum Key: TextSchema { - static let description = "Key of the example model" - static let example: String? = "my-example-key" - } + struct KeySchema: StringSchemaRepresentable { + var description: String? { "Key of the example model" } + var example: String? { "my-example-key" } + } - enum Create: ObjectSchema { - static let description = "example model create object" - static var properties: [ObjectSchemaProperty] { - [ - .init("key", Key.self) - ] - } + struct CreateSchema: ObjectSchemaRepresentable { + var description: String? { "example model create object" } + var propertyMap: SchemaMap { + [ + "key": KeySchema().reference() + ] } + } - enum Detail: ObjectSchema { - static let description = "example model detail object" - - static var properties: [ObjectSchemaProperty] { - [ - .init("id", Id.self), - .init("key", Key.self), - ] - } + struct DetailSchema: ObjectSchemaRepresentable { + var description: String? { "example model detail object" } + var propertyMap: SchemaMap { + [ + "id": IdSchema().reference(), + "key": KeySchema().reference(), + ] } + } - enum Patch: ObjectSchema { - static let description = "example model detail object" - - static var properties: [ObjectSchemaProperty] { - [ - .init("key", Key.self, required: false) - ] - } + struct PatchSchema: ObjectSchemaRepresentable { + var description: String? { "example model detail object" } + var propertyMap: SchemaMap { + [ + "key": KeySchema().reference(required: false) + ] } + } - enum List: ArraySchema { - - enum Item: ObjectSchema { - static let description = "example model detail object" - - static var properties: [ObjectSchemaProperty] { - [ - .init("id", Id.self), - .init("key", Key.self), - ] - } - } - - static let description = "Lorem ipsum dolor sit amet" - static var items: Schema.Type { Item.self } + struct ListItemSchema: ObjectSchemaRepresentable { + var description: String? { "example model detail object" } + var propertyMap: SchemaMap { + [ + "id": IdSchema().reference(), + "key": KeySchema().reference(), + ] } + } - enum PatchOverride: ObjectSchema { - static let id = Patch.id - static let override = true - - static let description = "overridden" + struct ListSchema: ArraySchemaRepresentable { + var description: String? { "Lorem ipsum dolor sit amet" } + var items: JSONSchema? { ListItemSchema().openAPISchema() } + } - static var properties: [ObjectSchemaProperty] { - [ - .init("key", Key.self, required: false) - ] - } + struct PatchOverrideSchema: ObjectSchemaRepresentable { + var description: String? { "overridden" } + var propertyMap: SchemaMap { + [ + "key": KeySchema().reference(required: false) + ] } } } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift index 8cec42a..9f5ab66 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift @@ -5,38 +5,20 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI import OpenAPIKit30 -// shared operation security -extension Operation { - - static func bearerToken() -> [OpenAPI.SecurityRequirement] { - Example.Model.SecuritySchemes.BearerToken.securityRequirement() - } -} - extension Example.Model { - static var securitySchemes: [SecurityScheme.Type] { - [ - SecuritySchemes.BearerToken.self - ] - } - - enum SecuritySchemes { - - enum BearerToken: SecurityScheme { - - static func openAPISecurityScheme() -> OpenAPI.SecurityScheme { - .init( - type: .http( - scheme: "bearer", - bearerFormat: "token" - ), - description: "Authorization header using a Bearer token" - ) - } + struct BearerTokenSecurityScheme: SecuritySchemeRepresentable { + var type: OpenAPI.SecurityScheme.SecurityType { + .http( + scheme: "bearer", + bearerFormat: "token" + ) + } + var description: String? { + "Authorization header using a Bearer token" } } } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Tags.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Tags.swift index 5c039e3..ac83491 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Tags.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Tags.swift @@ -5,20 +5,11 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension Example.Model { - static var tags: [Tag.Type] { - [ - Tags.Main.self - ] - } - - enum Tags { - - enum Main: Tag { - static let name = "Model" - } + struct ModelTag: TagRepresentable { + var name: String { "Model" } } } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel.swift index 1247143..b21731d 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel.swift @@ -5,28 +5,8 @@ // Created by Tibor Bodecs on 25/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension Example { - - enum Model: Component { - static func getComponentsOfType() -> [T] { - let prefixName = String(reflecting: self) + "." - return [ - Headers.self, - Operations.self, - Parameters.self, - PathItems.self, - RequestBodies.self, - Responses.self, - Schemas.self, - SecuritySchemes.self, - Tags.self, - ] - .compactMap { $0 as? T } - .filter { - String(reflecting: $0).hasPrefix(prefixName) - } - } - } + enum Model {} } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift index e2a744a..0fcd9e2 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift @@ -5,35 +5,22 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension ExampleDuplicatedItem.Model { - static var schemas: [Schema.Type] { - [ - Schemas.Id.self, - Schemas.Key.self, - Schemas.KeySecond.self, - ] + struct IdSchema: StringSchemaRepresentable { + var description: String? { "Unique example model identifier" } } - enum Schemas { - - enum Id: UUIDSchema { - static let description = "Unique example model identifier" - } - - enum Key: TextSchema { - static let description = "Key of the example model" - static let example: String? = "my-example-key" - - } - - enum KeySecond: TextSchema { - static let id = Key.id + struct KeySchema: StringSchemaRepresentable { + var description: String? { "Key of the example model" } + var example: String? { "my-example-key" } + } - static let description = "Key of the example model" - static let example: String? = "my-example-key" - } + struct KeySecondSchema: StringSchemaRepresentable { + var openAPIIdentifier: String { KeySchema().openAPIIdentifier } + var description: String? { "Key of the example model" } + var example: String? { "my-example-key" } } } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift index 0608ea4..2c8229e 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift @@ -5,21 +5,8 @@ // Created by Tibor Bodecs on 25/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension ExampleDuplicatedItem { - - enum Model: Component { - - static func getComponentsOfType() -> [T] { - let prefixName = String(reflecting: self) + "." - return [ - Schemas.self - ] - .compactMap { $0 as? T } - .filter { - String(reflecting: $0).hasPrefix(prefixName) - } - } - } + enum Model {} } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift index fd38c15..0bfbf7c 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift @@ -5,28 +5,16 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension ExampleMissingParentItem.Model { - static var schemas: [Schema.Type] { - [ - Schemas.Id.self, - Schemas.Key.self, - ] + struct IdSchema: StringSchemaRepresentable { + var description: String? { "Unique example model identifier" } } - enum Schemas { - - enum Id: UUIDSchema { - static let description = "Unique example model identifier" - } - - enum Key: TextSchema { - static let override = true - static let description = "Key of the example model" - static let example: String? = "my-example-key" - } - + struct KeySchema: StringSchemaRepresentable { + var description: String? { "Key of the example model" } + var example: String? { "my-example-key" } } } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift b/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift index 4a737a0..23d63c7 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift @@ -5,20 +5,8 @@ // Created by Tibor Bodecs on 25/01/2024. // -import FeatherOpenAPIKit +import FeatherOpenAPI extension ExampleMissingParentItem { - - enum Model: Component { - static func getComponentsOfType() -> [T] { - let prefixName = String(reflecting: self) + "." - return [ - Schemas.self - ] - .compactMap { $0 as? T } - .filter { - String(reflecting: $0).hasPrefix(prefixName) - } - } - } + enum Model {} } diff --git a/Tests/FeatherOpenAPIKitTests/Example/ExampleDocument.swift b/Tests/FeatherOpenAPIKitTests/Example/ExampleDocument.swift index 3c6cb14..45e4268 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/ExampleDocument.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/ExampleDocument.swift @@ -5,40 +5,70 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit -import OpenAPIKit30 -import Foundation +import FeatherOpenAPI -struct ExampleDocument: Document { +struct ExampleLocation: LocationRepresentable { + let location: String +} + +struct ExampleContact: ContactRepresentable { + var name: String? { "Binary Birds" } + var url: LocationRepresentable? { + ExampleLocation(location: "https://binarybirds.com") + } + var email: String? { "info@binarybirds.com" } +} + +struct ExampleInfo: InfoRepresentable { + var title: String { "Example" } + var description: String? { + """ + Example API description + """ + } + var contact: OpenAPIContactRepresentable? { ExampleContact() } + var version: String { "1.0.0" } +} - let components: [Component.Type] +struct ExampleServer: ServerRepresentable { + var url: LocationRepresentable { + ExampleLocation(location: "http://localhost:8080") + } + var description: String? { "dev" } +} - init() { - self.components = [ - Example.Model.self +struct ExamplePathCollection: PathCollectionRepresentable { + var pathMap: PathMap { + [ + "/example/models": Example.Model.MainPathItem(), + "/example/models/{id}": Example.Model.IdentifiedPathItem(), ] } +} - func openAPIDocument() throws -> OpenAPI.Document { - try composedDocument( - info: .init( - title: "Example", - description: """ - Example API description - """, - contact: .init( - name: "Binary Birds", - url: .init(string: "https://binarybirds.com")!, - email: "info@binarybirds.com" - ), - version: "1.0.0" - ), - servers: [ - .init( - url: .init(string: "http://localhost:8080")!, - description: "dev" - ) - ] +struct ExampleDocument: DocumentRepresentable { + var info: OpenAPIInfoRepresentable { ExampleInfo() } + var servers: [OpenAPIServerRepresentable] { [ExampleServer()] } + var paths: PathMap { ExamplePathCollection().pathMap } + var components: OpenAPIComponentsRepresentable { + let collection = ExamplePathCollection() + let bearer = Example.Model.BearerTokenSecurityScheme() + return Components( + schemas: collection.referencedSchemaMap, + parameters: collection.referencedParameterMap, + responses: collection.referencedResponseMap, + requestBodies: collection.referencedRequestBodyMap, + headers: collection.referencedHeaderMap, + securitySchemes: [bearer.reference().id: bearer] ) } + var security: [OpenAPISecurityRequirementRepresentable] { + [ + SecurityRequirement( + [ + (Example.Model.BearerTokenSecurityScheme().reference(), []) + ] + ) + ] + } } diff --git a/Tests/FeatherOpenAPIKitTests/Example/ExampleDuplicatedItemDocument.swift b/Tests/FeatherOpenAPIKitTests/Example/ExampleDuplicatedItemDocument.swift index 7e4cd0e..ac4e74d 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/ExampleDuplicatedItemDocument.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/ExampleDuplicatedItemDocument.swift @@ -5,39 +5,39 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit -import OpenAPIKit30 -import Foundation +import FeatherOpenAPI -struct ExampleDuplicatedItemDocument: Document { - - let components: [Component.Type] +struct ExampleDuplicatedItemInfo: InfoRepresentable { + var title: String { "ExampleDuplicatedItem" } + var description: String? { + """ + Example API description + """ + } + var contact: OpenAPIContactRepresentable? { ExampleContact() } + var version: String { "1.0.0" } +} - init() { - self.components = [ - ExampleDuplicatedItem.Model.self - ] +struct ExampleDuplicatedItemServer: ServerRepresentable { + var url: LocationRepresentable { + ExampleLocation(location: "http://localhost:8080") } + var description: String? { "dev" } +} - func openAPIDocument() throws -> OpenAPI.Document { - try composedDocument( - info: .init( - title: "ExampleDuplicatedItem", - description: """ - Example API description - """, - contact: .init( - name: "Binary Birds", - url: .init(string: "https://binarybirds.com")!, - email: "info@binarybirds.com" - ), - version: "1.0.0" - ), - servers: [ - .init( - url: .init(string: "http://localhost:8080")!, - description: "dev" - ) +struct ExampleDuplicatedItemDocument: DocumentRepresentable { + var info: OpenAPIInfoRepresentable { ExampleDuplicatedItemInfo() } + var servers: [OpenAPIServerRepresentable] { [ExampleDuplicatedItemServer()] } + var paths: PathMap { [:] } + var components: OpenAPIComponentsRepresentable { + let idSchema = ExampleDuplicatedItem.Model.IdSchema() + let keySchema = ExampleDuplicatedItem.Model.KeySchema() + let keySecondSchema = ExampleDuplicatedItem.Model.KeySecondSchema() + return Components( + schemas: [ + SchemaID(idSchema.openAPIIdentifier): idSchema, + SchemaID(keySchema.openAPIIdentifier): keySchema, + SchemaID(keySecondSchema.openAPIIdentifier): keySecondSchema, ] ) } diff --git a/Tests/FeatherOpenAPIKitTests/Example/ExampleMissingParentItemDocument.swift b/Tests/FeatherOpenAPIKitTests/Example/ExampleMissingParentItemDocument.swift index 58fb3f1..a6807d7 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/ExampleMissingParentItemDocument.swift +++ b/Tests/FeatherOpenAPIKitTests/Example/ExampleMissingParentItemDocument.swift @@ -5,39 +5,37 @@ // Created by Tibor Bodecs on 20/01/2024. // -import FeatherOpenAPIKit -import OpenAPIKit30 -import Foundation +import FeatherOpenAPI -struct ExampleMissingParentItemItemDocument: Document { - - let components: [Component.Type] +struct ExampleMissingParentItemInfo: InfoRepresentable { + var title: String { "ExampleMissingParentItem" } + var description: String? { + """ + Example API description + """ + } + var contact: OpenAPIContactRepresentable? { ExampleContact() } + var version: String { "1.0.0" } +} - init() { - self.components = [ - ExampleMissingParentItem.Model.self - ] +struct ExampleMissingParentItemServer: ServerRepresentable { + var url: LocationRepresentable { + ExampleLocation(location: "http://localhost:8080") } + var description: String? { "dev" } +} - func openAPIDocument() throws -> OpenAPI.Document { - try composedDocument( - info: .init( - title: "ExampleMissingParentItem", - description: """ - Example API description - """, - contact: .init( - name: "Binary Birds", - url: .init(string: "https://binarybirds.com")!, - email: "info@binarybirds.com" - ), - version: "1.0.0" - ), - servers: [ - .init( - url: .init(string: "http://localhost:8080")!, - description: "dev" - ) +struct ExampleMissingParentItemItemDocument: DocumentRepresentable { + var info: OpenAPIInfoRepresentable { ExampleMissingParentItemInfo() } + var servers: [OpenAPIServerRepresentable] { [ExampleMissingParentItemServer()] } + var paths: PathMap { [:] } + var components: OpenAPIComponentsRepresentable { + let idSchema = ExampleMissingParentItem.Model.IdSchema() + let keySchema = ExampleMissingParentItem.Model.KeySchema() + return Components( + schemas: [ + SchemaID(idSchema.openAPIIdentifier): idSchema, + SchemaID(keySchema.openAPIIdentifier): keySchema, ] ) } diff --git a/docker/tests/Dockerfile b/docker/tests/Dockerfile index 8fa3bbf..73be175 100644 --- a/docker/tests/Dockerfile +++ b/docker/tests/Dockerfile @@ -1,4 +1,4 @@ -FROM swift:6.2 +FROM swift:6.1 WORKDIR /app From 9c6d9620b19a1badf7645a076177ba3f3d0d7890 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sun, 25 Jan 2026 11:17:53 +0100 Subject: [PATCH 26/34] restructure examples --- Package.resolved | 6 +- Package.swift | 3 +- ...on.swift => OrderedDictionary+Merge.swift} | 0 .../Path.swift | 0 .../Abstraction/SchemaRepresentable.swift | 2 +- .../Schema/ArraySchemaRepresentable.swift | 15 +++- .../Schema/ObjectSchemaRepresentable.swift | 6 -- .../FeatherOpenAPIKit/DateTimeSchema.swift | 17 ---- Sources/FeatherOpenAPIKit/NumberSchema.swift | 37 -------- .../FeatherOpenAPIKitTests.swift | 85 ------------------- .../Example/Example.swift | 0 .../Example/ExampleDocument.swift | 29 ++----- .../Example}/ExampleDuplicatedItem.swift | 0 .../ExampleDuplicatedItemDocument.swift | 1 + .../ExampleDuplicatedItemModel+Schemas.swift | 0 .../Example}/ExampleDuplicatedItemModel.swift | 0 .../Example}/ExampleMissingParentItem.swift | 0 .../ExampleMissingParentItemDocument.swift | 1 + ...xampleMissingParentItemModel+Schemas.swift | 0 .../ExampleMissingParentItemModel.swift | 0 .../Example}/ExampleModel+Headers.swift | 0 .../Example}/ExampleModel+Operations.swift | 2 + .../Example}/ExampleModel+Parameters.swift | 0 .../Example}/ExampleModel+PathItems.swift | 0 .../Example}/ExampleModel+RequestBodies.swift | 2 +- .../Example}/ExampleModel+Responses.swift | 4 +- .../Example}/ExampleModel+Schemas.swift | 6 +- .../ExampleModel+SecuritySchemes.swift | 0 .../Example}/ExampleModel+Tags.swift | 0 .../Example}/ExampleModel.swift | 0 .../ExampleTestSuite.swift | 63 ++++++++++++++ .../Petstore/Pet/Pet+Schemas.swift | 4 +- ...KitTests.swift => PetstoreTestSuite.swift} | 4 +- .../Todo/TestObjects.swift | 40 +++++++++ ...nAPIKitTests.swift => TodoTestSuite.swift} | 43 +--------- 35 files changed, 145 insertions(+), 225 deletions(-) rename Sources/FeatherOpenAPI/{OrderedDictionary+Composition.swift => OrderedDictionary+Merge.swift} (100%) rename Sources/{FeatherOpenAPIKit => FeatherOpenAPI}/Path.swift (100%) delete mode 100644 Sources/FeatherOpenAPIKit/DateTimeSchema.swift delete mode 100644 Sources/FeatherOpenAPIKit/NumberSchema.swift delete mode 100644 Tests/FeatherOpenAPIKitTests/FeatherOpenAPIKitTests.swift rename Tests/{FeatherOpenAPIKitTests/Example/Components => FeatherOpenAPITests}/Example/Example.swift (100%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/ExampleDocument.swift (60%) rename Tests/{FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem => FeatherOpenAPITests/Example}/ExampleDuplicatedItem.swift (100%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/ExampleDuplicatedItemDocument.swift (98%) rename Tests/{FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model => FeatherOpenAPITests/Example}/ExampleDuplicatedItemModel+Schemas.swift (100%) rename Tests/{FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model => FeatherOpenAPITests/Example}/ExampleDuplicatedItemModel.swift (100%) rename Tests/{FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem => FeatherOpenAPITests/Example}/ExampleMissingParentItem.swift (100%) rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/Example/ExampleMissingParentItemDocument.swift (98%) rename Tests/{FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model => FeatherOpenAPITests/Example}/ExampleMissingParentItemModel+Schemas.swift (100%) rename Tests/{FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model => FeatherOpenAPITests/Example}/ExampleMissingParentItemModel.swift (100%) rename Tests/{FeatherOpenAPIKitTests/Example/Components/Example/Model => FeatherOpenAPITests/Example}/ExampleModel+Headers.swift (100%) rename Tests/{FeatherOpenAPIKitTests/Example/Components/Example/Model => FeatherOpenAPITests/Example}/ExampleModel+Operations.swift (94%) rename Tests/{FeatherOpenAPIKitTests/Example/Components/Example/Model => FeatherOpenAPITests/Example}/ExampleModel+Parameters.swift (100%) rename Tests/{FeatherOpenAPIKitTests/Example/Components/Example/Model => FeatherOpenAPITests/Example}/ExampleModel+PathItems.swift (100%) rename Tests/{FeatherOpenAPIKitTests/Example/Components/Example/Model => FeatherOpenAPITests/Example}/ExampleModel+RequestBodies.swift (75%) rename Tests/{FeatherOpenAPIKitTests/Example/Components/Example/Model => FeatherOpenAPITests/Example}/ExampleModel+Responses.swift (80%) rename Tests/{FeatherOpenAPIKitTests/Example/Components/Example/Model => FeatherOpenAPITests/Example}/ExampleModel+Schemas.swift (91%) rename Tests/{FeatherOpenAPIKitTests/Example/Components/Example/Model => FeatherOpenAPITests/Example}/ExampleModel+SecuritySchemes.swift (100%) rename Tests/{FeatherOpenAPIKitTests/Example/Components/Example/Model => FeatherOpenAPITests/Example}/ExampleModel+Tags.swift (100%) rename Tests/{FeatherOpenAPIKitTests/Example/Components/Example/Model => FeatherOpenAPITests/Example}/ExampleModel.swift (100%) create mode 100644 Tests/FeatherOpenAPITests/ExampleTestSuite.swift rename Tests/FeatherOpenAPITests/{PetstoreOpenAPIKitTests.swift => PetstoreTestSuite.swift} (87%) rename Tests/FeatherOpenAPITests/{FeatherOpenAPIKitTests.swift => TodoTestSuite.swift} (56%) diff --git a/Package.resolved b/Package.resolved index 79c9792..b04da86 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,13 +1,13 @@ { - "originHash" : "0c7d07317a3d86a614e3f4417365a7e94201f8451d0a7a96d9b66edc25ca7c6b", + "originHash" : "df64111ed452c60afbaa75cf2d9c28bb55f995528b996f33f93d70fa99af4850", "pins" : [ { "identity" : "openapikit", "kind" : "remoteSourceControl", "location" : "https://github.com/mattpolzin/OpenAPIKit", "state" : { - "revision" : "b71ac7b811af8a7ef9b3a1cfb20e9cff96f86451", - "version" : "4.3.1" + "revision" : "d267e29373b3d2ff395d3b46d8bf2085a9263f67", + "version" : "5.0.0-rc.2" } }, { diff --git a/Package.swift b/Package.swift index 9ca5245..2c987c9 100644 --- a/Package.swift +++ b/Package.swift @@ -34,8 +34,7 @@ let package = Package( .library(name: "FeatherOpenAPI", targets: ["FeatherOpenAPI"]), ], dependencies: [ -// .package(url: "https://github.com/mattpolzin/OpenAPIKit", exact: "5.0.0-rc.2"), - .package(url: "https://github.com/mattpolzin/OpenAPIKit", from: "4.3.0"), + .package(url: "https://github.com/mattpolzin/OpenAPIKit", exact: "5.0.0-rc.2"), .package(url: "https://github.com/jpsim/Yams", from: "6.2.0"), ], targets: [ diff --git a/Sources/FeatherOpenAPI/OrderedDictionary+Composition.swift b/Sources/FeatherOpenAPI/OrderedDictionary+Merge.swift similarity index 100% rename from Sources/FeatherOpenAPI/OrderedDictionary+Composition.swift rename to Sources/FeatherOpenAPI/OrderedDictionary+Merge.swift diff --git a/Sources/FeatherOpenAPIKit/Path.swift b/Sources/FeatherOpenAPI/Path.swift similarity index 100% rename from Sources/FeatherOpenAPIKit/Path.swift rename to Sources/FeatherOpenAPI/Path.swift diff --git a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift index cc35c39..b053d1f 100644 --- a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift @@ -31,6 +31,6 @@ public extension SchemaRepresentable { var deprecated: Bool? { nil } var referencedSchemaMap: OrderedDictionary { - [:] + return [:] } } diff --git a/Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift index bf86bde..f062ab0 100644 --- a/Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift @@ -10,7 +10,7 @@ import OpenAPIKit30 public protocol ArraySchemaRepresentable: SchemaRepresentable { - var items: JSONSchema? { get } + var items: SchemaRepresentable? { get } } public extension ArraySchemaRepresentable { @@ -29,11 +29,22 @@ public extension ArraySchemaRepresentable { minItems: nil, maxItems: nil, uniqueItems: nil, - items: items, + items: items?.openAPISchema(), allowedValues: nil, defaultValue: nil, example: nil ) } + + var referencedSchemaMap: OrderedDictionary { + var results: OrderedDictionary = [:] + + for (key, value) in items?.referencedSchemaMap ?? [:] { +// if let ref = value as? SchemaReferenceRepresentable { + results[key] = value +// } + } + return results + } } diff --git a/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift index 411d17e..28cd8a7 100644 --- a/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift @@ -41,12 +41,6 @@ public extension ObjectSchemaRepresentable { var referencedSchemaMap: OrderedDictionary { var results = OrderedDictionary() -// -// for property in propertyMap.values { -// if let ref = property as? SchemaReferenceRepresentable { -// results[ref.id] = ref.object -// } -// } for (_, value) in propertyMap { if let ref = value as? SchemaReferenceRepresentable { diff --git a/Sources/FeatherOpenAPIKit/DateTimeSchema.swift b/Sources/FeatherOpenAPIKit/DateTimeSchema.swift deleted file mode 100644 index d1ea3f1..0000000 --- a/Sources/FeatherOpenAPIKit/DateTimeSchema.swift +++ /dev/null @@ -1,17 +0,0 @@ -import OpenAPIKit30 - -public protocol DateTimeSchema: Schema { - static var example: String? { get } -} - -extension DateTimeSchema { - public static var example: String? { "2026-01-2T09:20:15.000Z" } - - public static func openAPISchema() -> JSONSchema { - .string( - format: .dateTime, - description: description, - example: example.map { .init(stringLiteral: $0) } - ) - } -} diff --git a/Sources/FeatherOpenAPIKit/NumberSchema.swift b/Sources/FeatherOpenAPIKit/NumberSchema.swift deleted file mode 100644 index 4c6fcb0..0000000 --- a/Sources/FeatherOpenAPIKit/NumberSchema.swift +++ /dev/null @@ -1,37 +0,0 @@ -public protocol NumberSchema: Schema { - associatedtype T - - static var defaultValue: T? { get } - static var example: T? { get } - - static var minimumValue: T? { get } - static var minimumExclusive: Bool { get } - static var minimum: (T, exclusive: Bool)? { get } - - static var maximumValue: T? { get } - static var maximumExclusive: Bool { get } - static var maximum: (T, exclusive: Bool)? { get } -} - -extension NumberSchema { - - public static var minimumValue: T? { nil } - public static var minimumExclusive: Bool { false } - public static var minimum: (T, exclusive: Bool)? { - if let value = minimumValue { - return (value, exclusive: minimumExclusive) - } - return nil - } - public static var maximumValue: T? { nil } - public static var maximumExclusive: Bool { false } - public static var maximum: (T, exclusive: Bool)? { - if let value = maximumValue { - return (value, exclusive: maximumExclusive) - } - return nil - } - - public static var defaultValue: T? { nil } - public static var example: T? { nil } -} diff --git a/Tests/FeatherOpenAPIKitTests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPIKitTests/FeatherOpenAPIKitTests.swift deleted file mode 100644 index 047e70f..0000000 --- a/Tests/FeatherOpenAPIKitTests/FeatherOpenAPIKitTests.swift +++ /dev/null @@ -1,85 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - -import Foundation -import OpenAPIKit30 -import OpenAPIKitCore -import Yams -import Testing - -@testable import FeatherOpenAPIKit - -@Suite -struct FeatherOpenAPIKitTests { - - @Test - func render() throws { - - let document = ExampleDocument() - - // #expect( - // try document.schemas() - // .contains { - // $0.key.rawValue == "ExampleModelPatch" - // && $0.value.description == "overridden" - // } - // ) - - let encoder = YAMLEncoder() - let openAPIDocument = try document.openAPIDocument() - do { - _ = try openAPIDocument.locallyDereferenced() - } - catch { - Issue.record("\(error)") - return - } - - let output = try encoder.encode(openAPIDocument) - - print(output) - } - - @Test - func duplicatedItem() throws { - - let document = ExampleDuplicatedItemDocument() - var errorMessage: String = "none" - - do { - let _ = try document.openAPIDocument() - } - catch let error as ComposeDocumentError { - errorMessage = error.message - } - - #expect( - errorMessage - == "Feather OpenAPI item id is duplicated: 'ExampleDuplicatedItemModelKey' (Did you forget to include override=true?)" - ) - } - - @Test - func missingParentItem() throws { - - let document = ExampleMissingParentItemItemDocument() - var errorMessage: String = "none" - - do { - let _ = try document.openAPIDocument() - } - catch let error as ComposeDocumentError { - errorMessage = error.message - } - - #expect( - errorMessage - == "Feather OpenAPI item 'ExampleMissingParentItemModelKey' is set as override but has no parent. (Are the component orders correct? Or are the IDs the same?)" - ) - } - -} diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Example.swift b/Tests/FeatherOpenAPITests/Example/Example.swift similarity index 100% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Example.swift rename to Tests/FeatherOpenAPITests/Example/Example.swift diff --git a/Tests/FeatherOpenAPIKitTests/Example/ExampleDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift similarity index 60% rename from Tests/FeatherOpenAPIKitTests/Example/ExampleDocument.swift rename to Tests/FeatherOpenAPITests/Example/ExampleDocument.swift index 45e4268..0cb05df 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/ExampleDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift @@ -6,6 +6,7 @@ // import FeatherOpenAPI +import OpenAPIKit30 struct ExampleLocation: LocationRepresentable { let location: String @@ -47,28 +48,12 @@ struct ExamplePathCollection: PathCollectionRepresentable { } struct ExampleDocument: DocumentRepresentable { + + let collection = ExamplePathCollection() + var info: OpenAPIInfoRepresentable { ExampleInfo() } var servers: [OpenAPIServerRepresentable] { [ExampleServer()] } - var paths: PathMap { ExamplePathCollection().pathMap } - var components: OpenAPIComponentsRepresentable { - let collection = ExamplePathCollection() - let bearer = Example.Model.BearerTokenSecurityScheme() - return Components( - schemas: collection.referencedSchemaMap, - parameters: collection.referencedParameterMap, - responses: collection.referencedResponseMap, - requestBodies: collection.referencedRequestBodyMap, - headers: collection.referencedHeaderMap, - securitySchemes: [bearer.reference().id: bearer] - ) - } - var security: [OpenAPISecurityRequirementRepresentable] { - [ - SecurityRequirement( - [ - (Example.Model.BearerTokenSecurityScheme().reference(), []) - ] - ) - ] - } + + var paths: PathMap { collection.pathMap } + var components: OpenAPIComponentsRepresentable { collection.components } } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/ExampleDuplicatedItem.swift b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItem.swift similarity index 100% rename from Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/ExampleDuplicatedItem.swift rename to Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItem.swift diff --git a/Tests/FeatherOpenAPIKitTests/Example/ExampleDuplicatedItemDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift similarity index 98% rename from Tests/FeatherOpenAPIKitTests/Example/ExampleDuplicatedItemDocument.swift rename to Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift index ac4e74d..d3ce66a 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/ExampleDuplicatedItemDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift @@ -6,6 +6,7 @@ // import FeatherOpenAPI +import OpenAPIKit30 struct ExampleDuplicatedItemInfo: InfoRepresentable { var title: String { "ExampleDuplicatedItem" } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel+Schemas.swift similarity index 100% rename from Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel+Schemas.swift rename to Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel+Schemas.swift diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel.swift similarity index 100% rename from Tests/FeatherOpenAPIKitTests/Example/Components/ExampleDuplicatedItem/Model/ExampleDuplicatedItemModel.swift rename to Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel.swift diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/ExampleMissingParentItem.swift b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItem.swift similarity index 100% rename from Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/ExampleMissingParentItem.swift rename to Tests/FeatherOpenAPITests/Example/ExampleMissingParentItem.swift diff --git a/Tests/FeatherOpenAPIKitTests/Example/ExampleMissingParentItemDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift similarity index 98% rename from Tests/FeatherOpenAPIKitTests/Example/ExampleMissingParentItemDocument.swift rename to Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift index a6807d7..ba1f01a 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/ExampleMissingParentItemDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift @@ -6,6 +6,7 @@ // import FeatherOpenAPI +import OpenAPIKit30 struct ExampleMissingParentItemInfo: InfoRepresentable { var title: String { "ExampleMissingParentItem" } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel+Schemas.swift similarity index 100% rename from Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel+Schemas.swift rename to Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel+Schemas.swift diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel.swift similarity index 100% rename from Tests/FeatherOpenAPIKitTests/Example/Components/ExampleMissingParentItem/Model/ExampleMissingParentItemModel.swift rename to Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel.swift diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Headers.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Headers.swift similarity index 100% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Headers.swift rename to Tests/FeatherOpenAPITests/Example/ExampleModel+Headers.swift diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Operations.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Operations.swift similarity index 94% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Operations.swift rename to Tests/FeatherOpenAPITests/Example/ExampleModel+Operations.swift index 10bc6ff..e555d9c 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Operations.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Operations.swift @@ -6,6 +6,7 @@ // import FeatherOpenAPI +import OpenAPIKit30 extension Example.Model { @@ -15,6 +16,7 @@ extension Example.Model { var description: String? { "Detail example detail" } var parameters: [ParameterRepresentable] { [ + IdParameter().reference(), CustomRequestHeaderParameter().reference() ] } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Parameters.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Parameters.swift similarity index 100% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Parameters.swift rename to Tests/FeatherOpenAPITests/Example/ExampleModel+Parameters.swift diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+PathItems.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+PathItems.swift similarity index 100% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+PathItems.swift rename to Tests/FeatherOpenAPITests/Example/ExampleModel+PathItems.swift diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+RequestBodies.swift similarity index 75% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift rename to Tests/FeatherOpenAPITests/Example/ExampleModel+RequestBodies.swift index b8c4c44..4a1e962 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+RequestBodies.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+RequestBodies.swift @@ -11,6 +11,6 @@ extension Example.Model { struct CreateRequestBody: JSONRequestBodyRepresentable { var description: String? { "Create example" } - var schema: CreateSchema { CreateSchema() } + var schema: SchemaReference { CreateSchema().reference() } } } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Responses.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Responses.swift similarity index 80% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Responses.swift rename to Tests/FeatherOpenAPITests/Example/ExampleModel+Responses.swift index 7ca5675..646d109 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Responses.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Responses.swift @@ -14,14 +14,14 @@ extension Example.Model { var description: String { "Example" } var contentMap: ContentMap { [ - .xml: Content(DetailSchema()) + .xml: Content(DetailSchema().reference()) ] } } struct DetailResponse: JSONResponseRepresentable { var description: String { "Example" } - var schema: DetailSchema { DetailSchema() } + var schema: SchemaReference { DetailSchema().reference() } var headerMap: HeaderMap { [ "X-Custom-Response-Header": CustomResponseHeader().reference() diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Schemas.swift similarity index 91% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Schemas.swift rename to Tests/FeatherOpenAPITests/Example/ExampleModel+Schemas.swift index 1bfb611..a7885a8 100644 --- a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Schemas.swift @@ -47,7 +47,7 @@ extension Example.Model { var description: String? { "example model detail object" } var propertyMap: SchemaMap { [ - "key": KeySchema().reference(required: false) + "key": KeySchema().reference() ] } } @@ -64,14 +64,14 @@ extension Example.Model { struct ListSchema: ArraySchemaRepresentable { var description: String? { "Lorem ipsum dolor sit amet" } - var items: JSONSchema? { ListItemSchema().openAPISchema() } + var items: SchemaRepresentable? { ListItemSchema() } } struct PatchOverrideSchema: ObjectSchemaRepresentable { var description: String? { "overridden" } var propertyMap: SchemaMap { [ - "key": KeySchema().reference(required: false) + "key": KeySchema().reference() ] } } diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+SecuritySchemes.swift similarity index 100% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+SecuritySchemes.swift rename to Tests/FeatherOpenAPITests/Example/ExampleModel+SecuritySchemes.swift diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Tags.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Tags.swift similarity index 100% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel+Tags.swift rename to Tests/FeatherOpenAPITests/Example/ExampleModel+Tags.swift diff --git a/Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel.swift similarity index 100% rename from Tests/FeatherOpenAPIKitTests/Example/Components/Example/Model/ExampleModel.swift rename to Tests/FeatherOpenAPITests/Example/ExampleModel.swift diff --git a/Tests/FeatherOpenAPITests/ExampleTestSuite.swift b/Tests/FeatherOpenAPITests/ExampleTestSuite.swift new file mode 100644 index 0000000..37f4120 --- /dev/null +++ b/Tests/FeatherOpenAPITests/ExampleTestSuite.swift @@ -0,0 +1,63 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 20/01/2024. +// + +import Foundation +import OpenAPIKit +import OpenAPIKit30 +import OpenAPIKitCompat +import Yams +import Testing + +@testable import FeatherOpenAPI + + + +@Suite +struct ExampleTestSuite { + + @Test + func render() throws { + + let document = ExampleDocument() + + let openAPIdoc = document.openAPIDocument() +// _ = try openAPIdoc.locallyDereferenced().resolved() + + let encoder = YAMLEncoder() + let result = try encoder.encode(openAPIdoc) + print("---- 3.0 ----") + print(result) + } + + @Test + func duplicatedItem() throws { + + let document = ExampleDuplicatedItemDocument() + + let openAPIdoc = document.openAPIDocument() + _ = try openAPIdoc.locallyDereferenced().resolved() + + let encoder = YAMLEncoder() + let result = try encoder.encode(openAPIdoc) + print("---- 3.0 ----") + print(result) + } + + @Test + func missingParentItem() throws { + + let document = ExampleMissingParentItemItemDocument() + + let openAPIdoc = document.openAPIDocument() + _ = try openAPIdoc.locallyDereferenced().resolved() + + let encoder = YAMLEncoder() + let result = try encoder.encode(openAPIdoc) + print("---- 3.0 ----") + print(result) + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Schemas.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Schemas.swift index 17abce0..afb8989 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Schemas.swift @@ -22,12 +22,12 @@ extension Petstore.Pet { } struct PhotoUrlsSchema: ArraySchemaRepresentable { - var items: JSONSchema? { PhotoUrlItemSchema().openAPISchema() } + var items: SchemaRepresentable? { PhotoUrlItemSchema() } } struct TagsSchema: ArraySchemaRepresentable { var required: Bool { false } - var items: JSONSchema? { Petstore.Tag.TagSchema().openAPISchema() } + var items: SchemaRepresentable? { Petstore.Tag.TagSchema() } } struct StatusSchema: StringSchemaRepresentable { diff --git a/Tests/FeatherOpenAPITests/PetstoreOpenAPIKitTests.swift b/Tests/FeatherOpenAPITests/PetstoreTestSuite.swift similarity index 87% rename from Tests/FeatherOpenAPITests/PetstoreOpenAPIKitTests.swift rename to Tests/FeatherOpenAPITests/PetstoreTestSuite.swift index f6e2035..3683a87 100644 --- a/Tests/FeatherOpenAPITests/PetstoreOpenAPIKitTests.swift +++ b/Tests/FeatherOpenAPITests/PetstoreTestSuite.swift @@ -14,7 +14,7 @@ import Testing @testable import FeatherOpenAPI @Suite -struct PetstoreOpenAPIKitTests { +struct PetstoreTestSuite { @Test func render() throws { @@ -22,7 +22,7 @@ struct PetstoreOpenAPIKitTests { let document = PetstoreDocument() let encoder = YAMLEncoder() - let openAPIDocument = try document.openAPIDocument() + let openAPIDocument = document.openAPIDocument() do { _ = diff --git a/Tests/FeatherOpenAPITests/Todo/TestObjects.swift b/Tests/FeatherOpenAPITests/Todo/TestObjects.swift index e7215ff..8916acc 100644 --- a/Tests/FeatherOpenAPITests/Todo/TestObjects.swift +++ b/Tests/FeatherOpenAPITests/Todo/TestObjects.swift @@ -8,6 +8,46 @@ import FeatherOpenAPI import OpenAPIKit30 +struct MyPathCollection: PathCollectionRepresentable { + + + var pathMap: PathMap { + [ + "todos": TodoPathItems(), +// "laci": LaciPathItems(), + ] + } +} + +struct MyInfo: InfoRepresentable { + var title: String { "foo" } + var version: String { "1.0.0" } +} + +struct MyDocument: DocumentRepresentable { + + var info: OpenAPIInfoRepresentable + + var servers: [any OpenAPIServerRepresentable] { + [ + TestServer() + ] + } + + var paths: PathMap + var components: OpenAPIComponentsRepresentable + + init( + info: OpenAPIInfoRepresentable, + paths: PathMap, + components: OpenAPIComponentsRepresentable + ) { + self.info = info + self.paths = paths + self.components = components + } +} + extension String: LocationRepresentable { public var location: String { self } } diff --git a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift b/Tests/FeatherOpenAPITests/TodoTestSuite.swift similarity index 56% rename from Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift rename to Tests/FeatherOpenAPITests/TodoTestSuite.swift index 370579d..1ec5fce 100644 --- a/Tests/FeatherOpenAPITests/FeatherOpenAPIKitTests.swift +++ b/Tests/FeatherOpenAPITests/TodoTestSuite.swift @@ -15,51 +15,14 @@ import Testing @testable import FeatherOpenAPI -struct MyInfo: InfoRepresentable { - var title: String { "foo" } - var version: String { "1.0.0" } -} - -struct MyDocument: DocumentRepresentable { - - var info: OpenAPIInfoRepresentable - - var servers: [any OpenAPIServerRepresentable] { - [ - TestServer() - ] - } - - var paths: PathMap - var components: OpenAPIComponentsRepresentable - - init( - info: OpenAPIInfoRepresentable, - paths: PathMap, - components: OpenAPIComponentsRepresentable - ) { - self.info = info - self.paths = paths - self.components = components - } -} @Suite -struct FeatherOpenAPIKitTests { - +struct TodoTestSuite { + @Test func example() throws { - struct MyPathCollection: PathCollectionRepresentable { - - - var pathMap: PathMap { - [ - "todos": TodoPathItems(), -// "laci": LaciPathItems(), - ] - } - } + let collection = MyPathCollection() // collection.components.schemas.register(id: "", TodoFieldId()) From 838cc1a9035af97996debfc8fcbfd2cc98784dbb Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sun, 25 Jan 2026 11:25:06 +0100 Subject: [PATCH 27/34] fix schema map references --- .../Content/ContentRepresentable.swift | 2 +- .../Header/HeaderRepresentable.swift | 7 +++--- .../Abstraction/ParameterRepresentable.swift | 7 +++--- .../Reference/SchemaReference.swift | 4 ++++ .../RequestBodyRepresentable.swift | 4 +--- .../Schema/Abstraction/SchemaMap.swift | 2 +- .../Abstraction/SchemaRepresentable.swift | 22 +++++++++++++++++++ .../Schema/ObjectSchemaRepresentable.swift | 6 +---- .../ExampleTestSuite.swift | 2 +- 9 files changed, 37 insertions(+), 19 deletions(-) diff --git a/Sources/FeatherOpenAPI/Content/ContentRepresentable.swift b/Sources/FeatherOpenAPI/Content/ContentRepresentable.swift index a06f1c8..c21b370 100644 --- a/Sources/FeatherOpenAPI/Content/ContentRepresentable.swift +++ b/Sources/FeatherOpenAPI/Content/ContentRepresentable.swift @@ -27,6 +27,6 @@ public extension ContentRepresentable { } var referencedSchemaMap: OrderedDictionary { - schema.referencedSchemaMap + schema.allReferencedSchemaMap() } } diff --git a/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift b/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift index 328ade8..ee91b4f 100644 --- a/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift +++ b/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift @@ -39,10 +39,9 @@ public extension HeaderRepresentable { } var referencedSchemaMap: OrderedDictionary { - var results = OrderedDictionary() - if let ref = schema as? SchemaReferenceRepresentable { - results[ref.id] = ref.object + guard let schema = schema as? SchemaRepresentable else { + return [:] } - return results + return schema.allReferencedSchemaMap() } } diff --git a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift index 6685a58..9f0b176 100644 --- a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift +++ b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift @@ -43,10 +43,9 @@ public extension ParameterRepresentable { } var referencedSchemaMap: OrderedDictionary { - var results = OrderedDictionary() - if let ref = schema as? SchemaReferenceRepresentable { - results[ref.id] = ref.object + guard let schema = schema as? SchemaRepresentable else { + return [:] } - return results + return schema.allReferencedSchemaMap() } } diff --git a/Sources/FeatherOpenAPI/Reference/SchemaReference.swift b/Sources/FeatherOpenAPI/Reference/SchemaReference.swift index a6a6afd..ff0db62 100644 --- a/Sources/FeatherOpenAPI/Reference/SchemaReference.swift +++ b/Sources/FeatherOpenAPI/Reference/SchemaReference.swift @@ -36,4 +36,8 @@ public struct SchemaReference: public func openAPISchema() -> JSONSchema { .reference(.component(named: id.rawValue), required: required) } + + public var referencedSchemaMap: OrderedDictionary { + [id: object] + } } diff --git a/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyRepresentable.swift index 9591a2b..4e2dbf3 100644 --- a/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyRepresentable.swift +++ b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyRepresentable.swift @@ -39,9 +39,7 @@ public extension RequestBodyRepresentable { var referencedSchemaMap: OrderedDictionary { var results = OrderedDictionary() for content in contentMap.values { - if let ref = content.schema as? SchemaReferenceRepresentable { - results[ref.id] = ref.object - } + results.merge(content.referencedSchemaMap) } return results } diff --git a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaMap.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaMap.swift index 55d62a8..bb2185b 100644 --- a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaMap.swift +++ b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaMap.swift @@ -10,5 +10,5 @@ import OpenAPIKit30 public typealias SchemaMap = OrderedDictionary < String, - OpenAPISchemaRepresentable + SchemaRepresentable > diff --git a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift index b053d1f..18f1f98 100644 --- a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift @@ -33,4 +33,26 @@ public extension SchemaRepresentable { var referencedSchemaMap: OrderedDictionary { return [:] } + + func allReferencedSchemaMap() -> OrderedDictionary { + var results = OrderedDictionary() + var visited = Set() + collectReferencedSchemaMap(into: &results, visited: &visited) + return results + } + + fileprivate func collectReferencedSchemaMap( + into results: inout OrderedDictionary, + visited: inout Set + ) { + for (id, schema) in referencedSchemaMap { + guard visited.insert(id).inserted else { + continue + } + results[id] = schema + if let schema = schema as? SchemaRepresentable { + schema.collectReferencedSchemaMap(into: &results, visited: &visited) + } + } + } } diff --git a/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift index 28cd8a7..05f2e97 100644 --- a/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift @@ -41,13 +41,9 @@ public extension ObjectSchemaRepresentable { var referencedSchemaMap: OrderedDictionary { var results = OrderedDictionary() - for (_, value) in propertyMap { - if let ref = value as? SchemaReferenceRepresentable { - results[ref.id] = ref.object - } + results.merge(value.referencedSchemaMap) } return results } } - diff --git a/Tests/FeatherOpenAPITests/ExampleTestSuite.swift b/Tests/FeatherOpenAPITests/ExampleTestSuite.swift index 37f4120..e81a128 100644 --- a/Tests/FeatherOpenAPITests/ExampleTestSuite.swift +++ b/Tests/FeatherOpenAPITests/ExampleTestSuite.swift @@ -25,7 +25,7 @@ struct ExampleTestSuite { let document = ExampleDocument() let openAPIdoc = document.openAPIDocument() -// _ = try openAPIdoc.locallyDereferenced().resolved() + _ = try openAPIdoc.locallyDereferenced().resolved() let encoder = YAMLEncoder() let result = try encoder.encode(openAPIdoc) From a6f4f1b1701d77dd08ca17b6174659d7249d9671 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sun, 25 Jan 2026 11:34:35 +0100 Subject: [PATCH 28/34] fix openid identifiers --- Sources/FeatherOpenAPI/Identifiable.swift | 24 +++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/Sources/FeatherOpenAPI/Identifiable.swift b/Sources/FeatherOpenAPI/Identifiable.swift index 3c64593..f988d00 100644 --- a/Sources/FeatherOpenAPI/Identifiable.swift +++ b/Sources/FeatherOpenAPI/Identifiable.swift @@ -12,17 +12,21 @@ public protocol Identifiable: Sendable { public extension Identifiable { var openAPIIdentifier: String { - String(describing: type(of: self)) + let name = String(reflecting: type(of: self)) + var components = name.split(separator: ".") + if components.count > 1 { + components.remove(at: 0) // remove namespace if present + } + let identifier = components + .joined(separator: "") + .replacing("()", with: "") + .replacing("GenericComponent", with: "Generic") + + return identifier } } -//static var id: String { -// var components = String(reflecting: self).split(separator: ".") -// components.remove(at: 0) // remove namespace -// components.remove(at: 2) // remove enum name -// return -// components -// .joined(separator: "") -// .replacing("GenericComponent", with: "Generic") -//} + + + From cfe1387bd3031a86c08e906e155272250bd838c9 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sun, 25 Jan 2026 17:29:15 +0100 Subject: [PATCH 29/34] fixes & docs --- .github/workflows/deployment.yml | 16 +++ .github/workflows/run-checks.yml | 26 ---- .github/workflows/run-tests.yml | 54 -------- .github/workflows/testing.yml | 39 ++++++ .swift-format | 114 ++++++++-------- .swiftformatignore | 2 +- AGENTS.md | 6 + .../CommandBuilder.swift | 62 --------- .../FeatherOpenAPIGenerator/Entrypoint.swift | 51 ------- .../FeatherOpenAPI/Callback/CallbackID.swift | 6 +- .../Components/Components.swift | 49 +++++-- .../Components/ComponentsRepresentable.swift | 99 +++++++++++--- .../OpenAPIComponentsRepresentable.swift | 8 +- .../Contact/ContactRepresentable.swift | 25 ++-- .../Contact/OpenAPIContactRepresentable.swift | 7 +- Sources/FeatherOpenAPI/Content/Content.swift | 8 +- .../FeatherOpenAPI/Content/ContentMap.swift | 6 +- .../Content/ContentRepresentable.swift | 17 ++- .../Content/OpenAPIContentRepresentable.swift | 11 +- .../Document/DocumentRepresentable.swift | 37 +++-- .../OpenAPIDocumentRepresentable.swift | 7 +- .../FeatherOpenAPI/Example/ExampleID.swift | 6 +- .../Example/ExampleRepresentable.swift | 19 ++- .../Example/OpenAPIExampleRepresentable.swift | 15 +- .../ExternalDocsRepresentable.swift | 11 +- .../OpenAPIExternalDocsRepresentable.swift | 8 +- Sources/FeatherOpenAPI/Header/HeaderID.swift | 6 +- Sources/FeatherOpenAPI/Header/HeaderMap.swift | 7 +- .../Header/HeaderRepresentable.swift | 25 +++- .../Header/OpenAPIHeaderRepresentable.swift | 15 +- Sources/FeatherOpenAPI/Identifiable.swift | 17 ++- .../Info/InfoRepresentable.swift | 23 +++- .../Info/OpenAPIInfoRepresentable.swift | 7 +- .../License/LicenseRepresentable.swift | 7 +- .../License/OpenAPILicenseRepresentable.swift | 7 +- Sources/FeatherOpenAPI/Link/LinkID.swift | 6 +- .../Link/OpenAPILinkRepresentable.swift | 7 +- .../Location/LocationRepresentable.swift | 11 +- .../OpenAPILocationRepresentable.swift | 7 +- .../OpenAPIOperationRepresentable.swift | 7 +- .../Operation/OperationRepresentable.swift | 128 ++++++++++++------ .../OrderedDictionary+Merge.swift | 8 +- .../OpenAPIParameterRepresentable.swift | 17 ++- .../Parameter/Abstraction/ParameterID.swift | 6 +- .../Abstraction/ParameterRepresentable.swift | 31 +++-- .../CookieParameterRepresentable.swift | 10 +- .../HeaderParameterRepresentable.swift | 12 +- .../PathParameterRepresentable.swift | 10 +- .../QueryParameterRepresentable.swift | 14 +- Sources/FeatherOpenAPI/Path.swift | 34 ++++- .../PathCollectionRepresentable.swift | 65 ++++++--- .../OpenAPIPathItemRepresentable.swift | 9 +- .../PathItem/PathItemRepresentable.swift | 127 +++++++++++------ Sources/FeatherOpenAPI/PathItem/PathMap.swift | 6 +- .../ExampleReferenceRepresentable.swift | 23 +++- .../HeaderReferenceRepresentable.swift | 18 ++- .../ParameterReferenceRepresentable.swift | 26 +++- .../ReferencedSchemaMapRepresentable.swift | 40 +++++- .../RequestBodyReferenceRepresentable.swift | 16 ++- .../ResponseReferenceRepresentable.swift | 18 ++- .../Reference/SchemaReference.swift | 19 ++- .../OpenAPIRequestBodyRepresentable.swift | 17 ++- .../Abstraction/RequestBodyID.swift | 6 +- .../RequestBodyRepresentable.swift | 25 +++- .../BinaryRequestBodyRepresentable.swift | 8 +- .../FormRequestBodyRepresentable.swift | 13 +- .../JSONRequestBodyRepresentable.swift | 13 +- .../OpenAPIResponseRepresentable.swift | 15 +- .../Response/Abstraction/ResponseID.swift | 6 +- .../Response/Abstraction/ResponseMap.swift | 6 +- .../Abstraction/ResponseRepresentable.swift | 23 +++- .../BinaryResponseRepresentable.swift | 9 +- .../Response/FormResponseRepresentable.swift | 13 +- .../Response/JSONResponseRepresentable.swift | 13 +- .../OpenAPISchemaRepresentable.swift | 9 +- .../Schema/Abstraction/SchemaID.swift | 6 +- .../Schema/Abstraction/SchemaMap.swift | 6 +- .../Abstraction/SchemaRepresentable.swift | 45 ++++-- .../Schema/ArraySchemaRepresentable.swift | 27 ++-- .../FeatherOpenAPI/Schema/BinarySchema.swift | 8 +- .../Schema/BoolSchemaRepresentable.swift | 12 +- .../Schema/DoubleSchemaRepresentable.swift | 11 +- .../Schema/FloatSchemaRepresentable.swift | 12 +- .../Schema/Int32SchemaRepresentable.swift | 11 +- .../Schema/Int64SchemaRepresentable.swift | 11 +- .../Schema/IntSchemaRepresentable.swift | 11 +- .../Schema/ObjectSchemaRepresentable.swift | 17 ++- .../Schema/StringSchemaRepresentable.swift | 12 +- ...nAPISecurityRequirementRepresentable.swift | 7 +- .../SecurityRequirementRepresentable.swift | 14 +- .../OpenAPISecuritySchemeRepresentable.swift | 9 +- .../SecuritySchemeRepresentable.swift | 12 +- .../Server/OpenAPIServerRepresentable.swift | 9 +- .../Server/ServerRepresentable.swift | 20 ++- .../Tag/OpenAPITagRepresentable.swift | 7 +- .../FeatherOpenAPI/Tag/TagRepresentable.swift | 17 ++- .../OpenAPIVariableRepresentable.swift | 9 +- .../FeatherOpenAPI/Variable/VariableMap.swift | 3 +- .../Variable/VariableRepresentable.swift | 11 +- .../_Properties/AllowedValuesProperty.swift | 12 +- .../_Properties/DefaultValueProperty.swift | 12 +- .../_Properties/DeprecatedProperty.swift | 9 +- .../_Properties/DescriptionProperty.swift | 9 +- .../_Properties/ExampleProperty.swift | 12 +- .../_Properties/NullableProperty.swift | 10 +- .../_Properties/RequiredProperty.swift | 9 +- .../_Properties/TitleProperty.swift | 9 +- .../VendorExtensionsProperty.swift | 11 +- .../FeatherOpenAPITests/Example/Example.swift | 6 + .../Example/ExampleDocument.swift | 10 +- .../Example/ExampleDuplicatedItem.swift | 6 + .../ExampleDuplicatedItemDocument.swift | 10 +- .../ExampleDuplicatedItemModel+Schemas.swift | 6 + .../Example/ExampleDuplicatedItemModel.swift | 6 + .../Example/ExampleMissingParentItem.swift | 6 + .../ExampleMissingParentItemDocument.swift | 10 +- ...xampleMissingParentItemModel+Schemas.swift | 6 + .../ExampleMissingParentItemModel.swift | 6 + .../Example/ExampleModel+Headers.swift | 6 + .../Example/ExampleModel+Operations.swift | 8 +- .../Example/ExampleModel+Parameters.swift | 6 + .../Example/ExampleModel+PathItems.swift | 6 + .../Example/ExampleModel+RequestBodies.swift | 6 + .../Example/ExampleModel+Responses.swift | 6 + .../Example/ExampleModel+Schemas.swift | 6 + .../ExampleModel+SecuritySchemes.swift | 6 + .../Example/ExampleModel+Tags.swift | 6 + .../Example/ExampleModel.swift | 6 + .../ExampleTestSuite.swift | 18 ++- .../PathComponentTests.swift | 16 ++- .../ApiResponse/ApiResponse+Schemas.swift | 2 +- .../Petstore/ApiResponse/ApiResponse.swift | 2 +- .../Petstore/Category/Category+Schemas.swift | 2 +- .../Petstore/Category/Category.swift | 2 +- .../Petstore/Pet/Pet+Operations.swift | 6 +- .../Petstore/Pet/Pet+Parameters.swift | 2 +- .../Petstore/Pet/Pet+PathItems.swift | 2 +- .../Petstore/Pet/Pet+Responses.swift | 2 +- .../Petstore/Pet/Pet+Schemas.swift | 5 +- .../Petstore/Pet/Pet+Tags.swift | 2 +- .../Petstore/Pet/Pet.swift | 2 +- .../Petstore/Petstore.swift | 6 +- .../Petstore/Store/Store+Schemas.swift | 2 +- .../Petstore/Store/Store.swift | 2 +- .../Petstore/Tag/Tag+Schemas.swift | 2 +- .../Petstore/Tag/Tag.swift | 2 +- .../Petstore/User/User+Schemas.swift | 2 +- .../Petstore/User/User.swift | 2 +- .../PetstoreTestSuite.swift | 4 +- .../Todo/TestObjects.swift | 39 +++--- Tests/FeatherOpenAPITests/TodoTestSuite.swift | 39 +++--- 151 files changed, 1567 insertions(+), 804 deletions(-) create mode 100644 .github/workflows/deployment.yml delete mode 100644 .github/workflows/run-checks.yml delete mode 100644 .github/workflows/run-tests.yml create mode 100644 .github/workflows/testing.yml delete mode 100644 Plugins/FeatherOpenAPIGenerator/CommandBuilder.swift delete mode 100644 Plugins/FeatherOpenAPIGenerator/Entrypoint.swift rename Tests/{FeatherOpenAPIKitTests => FeatherOpenAPITests}/PathComponentTests.swift (89%) diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml new file mode 100644 index 0000000..680623b --- /dev/null +++ b/.github/workflows/deployment.yml @@ -0,0 +1,16 @@ +name: Deployment + +on: + push: + tags: + - 'v*' + - '[0-9]*' + +jobs: + + create-docc-and-deploy: + uses: BinaryBirds/github-workflows/.github/workflows/docc_deploy.yml@main + permissions: + contents: read + pages: write + id-token: write \ No newline at end of file diff --git a/.github/workflows/run-checks.yml b/.github/workflows/run-checks.yml deleted file mode 100644 index b10006f..0000000 --- a/.github/workflows/run-checks.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Run checks - -on: - pull_request: - branches: - - main - -jobs: - - run-checks: - # runs-on: macOS-latest - runs-on: self-hosted - steps: - - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - - name: Install Dependencies - run: | - brew install mint - mint install NickLockwood/SwiftFormat@0.53.4 --no-link - - - name: run script - run: ./scripts/run-checks.sh diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml deleted file mode 100644 index d53cde7..0000000 --- a/.github/workflows/run-tests.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Run tests - -on: - push: - branches: - - main - paths: - - '**.swift' - pull_request: - branches: - - main - -jobs: - - macOS-tests: - runs-on: self-hosted - steps: - - - name: Checkout - uses: actions/checkout@v4 - - # - name: Cache - # uses: actions/cache@v3 - # with: - # path: server/.build - # key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} - # restore-keys: ${{ runner.os }}-spm- - - - name: Test - run: swift test --parallel --enable-code-coverage - - linux-tests: - runs-on: ubuntu-latest - strategy: - matrix: - image: - - 'swift:5.9' - - 'swift:5.10' - container: - image: ${{ matrix.image }} - steps: - - - name: Checkout - uses: actions/checkout@v4 - - # - name: Cache - # uses: actions/cache@v3 - # with: - # path: server/.build - # key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} - # restore-keys: ${{ runner.os }}-spm- - - - name: Test - run: swift test --parallel --enable-code-coverage diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml new file mode 100644 index 0000000..ebfae0b --- /dev/null +++ b/.github/workflows/testing.yml @@ -0,0 +1,39 @@ +name: Testing + +on: + pull_request: + branches: + - main + +jobs: + + swiftlang_checks: + name: Swiftlang Checks + uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main + with: + license_header_check_project_name: "project" + format_check_enabled : true + broken_symlink_check_enabled : true + unacceptable_language_check_enabled : true + shell_check_enabled : true + docs_check_enabled : false + api_breakage_check_enabled : false + license_header_check_enabled : false + yamllint_check_enabled : false + python_lint_check_enabled : false + + bb_checks: + name: BB Checks + uses: BinaryBirds/github-workflows/.github/workflows/extra_soundness.yml@main + with: + local_swift_dependencies_check_enabled : true + headers_check_enabled : true + docc_warnings_check_enabled : true + + swiftlang_tests: + name: Swiftlang Tests + uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main + with: + enable_windows_checks : false + linux_build_command: "swift test --parallel --enable-code-coverage" + linux_exclude_swift_versions: "[{\"swift_version\": \"5.8\"}, {\"swift_version\": \"5.9\"}, {\"swift_version\": \"5.10\"}, {\"swift_version\": \"nightly\"}, {\"swift_version\": \"nightly-main\"}, {\"swift_version\": \"6.0\"}, {\"swift_version\": \"nightly-6.0\"}, {\"swift_version\": \"nightly-6.1\"}, {\"swift_version\": \"nightly-6.3\"}]" \ No newline at end of file diff --git a/.swift-format b/.swift-format index e3cbb89..6a37415 100644 --- a/.swift-format +++ b/.swift-format @@ -1,58 +1,64 @@ { - "fileScopedDeclarationPrivacy" : { - "accessLevel" : "private" + "version": 1, + "lineLength": 80, + "maximumBlankLines": 1, + "fileScopedDeclarationPrivacy": { + "accessLevel": "private" }, - "indentation" : { - "spaces" : 4 + "tabWidth": 4, + "indentation": { + "spaces": 4 }, - "indentConditionalCompilationBlocks" : false, - "indentSwitchCaseLabels" : false, - "lineBreakAroundMultilineExpressionChainComponents" : true, - "lineBreakBeforeControlFlowKeywords" : true, - "lineBreakBeforeEachArgument" : true, - "lineBreakBeforeEachGenericRequirement" : true, - "lineLength" : 80, - "maximumBlankLines" : 1, - "prioritizeKeepingFunctionOutputTogether" : false, - "respectsExistingLineBreaks" : true, - "rules" : { - "AllPublicDeclarationsHaveDocumentation" : false, - "AlwaysUseLowerCamelCase" : false, - "AmbiguousTrailingClosureOverload" : true, - "BeginDocumentationCommentWithOneLineSummary" : false, - "DoNotUseSemicolons" : true, - "DontRepeatTypeInStaticProperties" : false, - "FileScopedDeclarationPrivacy" : true, - "FullyIndirectEnum" : true, - "GroupNumericLiterals" : true, - "IdentifiersMustBeASCII" : true, - "NeverForceUnwrap" : false, - "NeverUseForceTry" : false, - "NeverUseImplicitlyUnwrappedOptionals" : false, - "NoAccessLevelOnExtensionDeclaration" : false, - "NoAssignmentInExpressions" : true, - "NoBlockComments" : true, - "NoCasesWithOnlyFallthrough" : true, - "NoEmptyTrailingClosureParentheses" : true, - "NoLabelsInCasePatterns" : false, - "NoLeadingUnderscores" : false, - "NoParensAroundConditions" : true, - "NoVoidReturnOnFunctionSignature" : true, - "OneCasePerLine" : true, - "OneVariableDeclarationPerLine" : true, - "OnlyOneTrailingClosureArgument" : true, - "OrderedImports" : false, - "ReturnVoidInsteadOfEmptyTuple" : true, - "UseEarlyExits" : false, - "UseLetInEveryBoundCaseVariable" : false, - "UseShorthandTypeNames" : true, - "UseSingleLinePropertyGetter" : false, - "UseSynthesizedInitializer" : true, - "UseTripleSlashForDocumentationComments" : true, - "UseWhereClausesInForLoops" : false, - "ValidateDocumentationComments" : true - }, - "spacesAroundRangeFormationOperators" : false, - "tabWidth" : 4, - "version" : 1 + "indentConditionalCompilationBlocks": false, + "indentSwitchCaseLabels": false, + "lineBreakAroundMultilineExpressionChainComponents": true, + "lineBreakBeforeControlFlowKeywords": true, + "lineBreakBeforeEachArgument": true, + "lineBreakBeforeEachGenericRequirement": true, + "prioritizeKeepingFunctionOutputTogether": false, + "respectsExistingLineBreaks": true, + "spacesAroundRangeFormationOperators": false, + "multiElementCollectionTrailingCommas": true, + "rules": { + "AllPublicDeclarationsHaveDocumentation": false, + "AlwaysUseLiteralForEmptyCollectionInit": true, + "AlwaysUseLowerCamelCase": false, + "AmbiguousTrailingClosureOverload": true, + "BeginDocumentationCommentWithOneLineSummary": true, + "DoNotUseSemicolons": true, + "DontRepeatTypeInStaticProperties": true, + "FileScopedDeclarationPrivacy": true, + "FullyIndirectEnum": true, + "GroupNumericLiterals": true, + "IdentifiersMustBeASCII": true, + "NeverForceUnwrap": false, + "NeverUseForceTry": true, + "NeverUseImplicitlyUnwrappedOptionals": true, + "NoAccessLevelOnExtensionDeclaration": true, + "NoAssignmentInExpressions": true, + "NoBlockComments": true, + "NoCasesWithOnlyFallthrough": true, + "NoEmptyTrailingClosureParentheses": true, + "NoLabelsInCasePatterns": true, + "NoLeadingUnderscores": false, + "NoParensAroundConditions": true, + "NoPlaygroundLiterals": true, + "NoVoidReturnOnFunctionSignature": true, + "OmitExplicitReturns": true, + "OneCasePerLine": true, + "OneVariableDeclarationPerLine": true, + "OnlyOneTrailingClosureArgument": true, + "OrderedImports": true, + "ReplaceForEachWithForLoop": true, + "ReturnVoidInsteadOfEmptyTuple": true, + "TypeNamesShouldBeCapitalized": true, + "UseEarlyExits": true, + "UseLetInEveryBoundCaseVariable": true, + "UseShorthandTypeNames": true, + "UseSingleLinePropertyGetter": true, + "UseSynthesizedInitializer": true, + "UseTripleSlashForDocumentationComments": true, + "UseWhereClausesInForLoops": true, + "ValidateDocumentationComments": true + } } diff --git a/.swiftformatignore b/.swiftformatignore index c73cf0c..4308420 100644 --- a/.swiftformatignore +++ b/.swiftformatignore @@ -1 +1 @@ -Package.swift \ No newline at end of file +Package.swift diff --git a/AGENTS.md b/AGENTS.md index 157e1a4..d5c5226 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,3 +1,9 @@ +// +// AGENTS.md +// feather-openapi +// +// Created by Binary Birds on 2026. 01. 20.. + # Repository Guidelines This repository contains an project written with Swift 6. Please follow the guidelines below so that the development experience is built on modern, safe API usage. diff --git a/Plugins/FeatherOpenAPIGenerator/CommandBuilder.swift b/Plugins/FeatherOpenAPIGenerator/CommandBuilder.swift deleted file mode 100644 index 05a75fd..0000000 --- a/Plugins/FeatherOpenAPIGenerator/CommandBuilder.swift +++ /dev/null @@ -1,62 +0,0 @@ -// -// CommandBuilder.swift -// feather-openapi -// -// Created by Tibor Bödecs on 2026. 01. 20.. -// - -#if canImport(FoundationEssentials) -import FoundationEssentials -#else -import Foundation -#endif - -import PackagePlugin - -struct CommandBuilder { - - func createBuildCommands( - pluginWorkDirectoryURL: URL, - tool: (String) throws -> PluginContext.Tool, - sourceFiles: FileList, - targetName: String - ) throws -> [Command] { - let sourceDir = longestCommonFolderPath( - sourceFiles.map { $0.url.path() } - ) - let output = pluginWorkDirectoryURL.appending( - path: "Component+Generated.swift" - ) - - return [ - .buildCommand( - displayName: "Generate component extension code", - executable: try tool("feather-openapikit-generator").url, - arguments: [sourceDir, output.path(), targetName], - environment: [:], - inputFiles: [], - outputFiles: [output] - ) - ] - } - - func longestCommonFolderPath( - _ filePaths: [String] - ) -> String { - guard !filePaths.isEmpty else { return "" } - - var commonComponents = filePaths.first!.components(separatedBy: "/") - commonComponents.removeLast() - - for path in filePaths { - let components = path.components(separatedBy: "/") - for i in 0.. [Command] { - guard let swiftTarget = target as? SwiftSourceModuleTarget else { - throw PluginError.noTarget(name: target.name) - } - let commandBuilder = CommandBuilder() - return try commandBuilder.createBuildCommands( - pluginWorkDirectoryURL: context.pluginWorkDirectoryURL, - tool: context.tool, - sourceFiles: swiftTarget.sourceFiles, - targetName: target.name - ) - } -} - -#if canImport(XcodeProjectPlugin) -import XcodeProjectPlugin - -extension Entrypoint: XcodeBuildToolPlugin { - - func createBuildCommands( - context: XcodePluginContext, - target: XcodeTarget - ) throws -> [Command] { - let commandBuilder = CommandBuilder() - return try commandBuilder.createBuildCommands( - pluginWorkDirectoryURL: context.pluginWorkDirectoryURL, - tool: context.tool, - sourceFiles: target.inputFiles, - targetName: target.displayName - ) - } -} -#endif diff --git a/Sources/FeatherOpenAPI/Callback/CallbackID.swift b/Sources/FeatherOpenAPI/Callback/CallbackID.swift index d1625b7..fd45e4a 100644 --- a/Sources/FeatherOpenAPI/Callback/CallbackID.swift +++ b/Sources/FeatherOpenAPI/Callback/CallbackID.swift @@ -1,14 +1,18 @@ // -// File.swift +// CallbackID.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. // +/// Strongly typed identifier for reusable OpenAPI callbacks. public struct CallbackID: Sendable, Equatable, Hashable, Codable { + /// The raw identifier value. public var rawValue: String + /// Creates a callback identifier. + /// - Parameter rawValue: The raw identifier value. public init( _ rawValue: String ) { diff --git a/Sources/FeatherOpenAPI/Components/Components.swift b/Sources/FeatherOpenAPI/Components/Components.swift index 5faf87c..b15daad 100644 --- a/Sources/FeatherOpenAPI/Components/Components.swift +++ b/Sources/FeatherOpenAPI/Components/Components.swift @@ -1,5 +1,5 @@ // -// File.swift +// Components.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,23 +7,52 @@ import OpenAPIKit30 +/// Concrete container for reusable OpenAPI components. public struct Components: ComponentsRepresentable { - + + /// Schema component map. public var schemas: OrderedDictionary - public var parameters: OrderedDictionary - public var examples: OrderedDictionary - public var responses: OrderedDictionary - public var requestBodies: OrderedDictionary + /// Parameter component map. + public var parameters: + OrderedDictionary + /// Example component map. + public var examples: + OrderedDictionary + /// Response component map. + public var responses: + OrderedDictionary + /// Request body component map. + public var requestBodies: + OrderedDictionary + /// Header component map. public var headers: OrderedDictionary + /// Security requirement list used by components. public var securityRequirements: [SecurityRequirementRepresentable] + /// Link component map. public var links: OrderedDictionary + /// Creates a components container. + /// - Parameters: + /// - schemas: Schema component map. + /// - parameters: Parameter component map. + /// - examples: Example component map. + /// - responses: Response component map. + /// - requestBodies: Request body component map. + /// - headers: Header component map. + /// - securityRequirements: Security requirements. + /// - links: Link component map. public init( schemas: OrderedDictionary = [:], - parameters: OrderedDictionary = [:], - examples: OrderedDictionary = [:], - responses: OrderedDictionary = [:], - requestBodies: OrderedDictionary = [:], + parameters: OrderedDictionary< + ParameterID, OpenAPIParameterRepresentable + > = [:], + examples: OrderedDictionary = + [:], + responses: OrderedDictionary = + [:], + requestBodies: OrderedDictionary< + RequestBodyID, OpenAPIRequestBodyRepresentable + > = [:], headers: OrderedDictionary = [:], securityRequirements: [SecurityRequirementRepresentable] = [], links: OrderedDictionary = [:], diff --git a/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift b/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift index cefe98e..9a60c76 100644 --- a/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift +++ b/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// ComponentsRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -7,33 +7,69 @@ import OpenAPIKit30 +/// A type that can describe reusable OpenAPI components. public protocol ComponentsRepresentable: OpenAPIComponentsRepresentable, VendorExtensionsProperty { + /// Schema component map. var schemas: OrderedDictionary { get } - var parameters: OrderedDictionary { get } - var examples: OrderedDictionary { get } - var responses: OrderedDictionary { get } - var requestBodies: OrderedDictionary { get } + /// Parameter component map. + var parameters: + OrderedDictionary + { get } + /// Example component map. + var examples: OrderedDictionary { + get + } + /// Response component map. + var responses: OrderedDictionary { + get + } + /// Request body component map. + var requestBodies: + OrderedDictionary + { get } + /// Header component map. var headers: OrderedDictionary { get } + /// Security requirements used by the document. var securityRequirements: [SecurityRequirementRepresentable] { get } + /// Link component map. var links: OrderedDictionary { get } // public var callbacks: OrderedDictionary + /// Produces the OpenAPI schema components. + /// - Returns: A component dictionary of JSON schemas. func openAPISchemas() -> OpenAPI.ComponentDictionary + /// Produces the OpenAPI parameter components. + /// - Returns: A component dictionary of parameters. func openAPIParameters() -> OpenAPI.ComponentDictionary + /// Produces the OpenAPI example components. + /// - Returns: A component dictionary of examples. func openAPIExamples() -> OpenAPI.ComponentDictionary + /// Produces the OpenAPI response components. + /// - Returns: A component dictionary of responses. func openAPIResponses() -> OpenAPI.ComponentDictionary + /// Produces the OpenAPI request body components. + /// - Returns: A component dictionary of requests. func openAPIRequestBodies() -> OpenAPI.ComponentDictionary + /// Produces the OpenAPI header components. + /// - Returns: A component dictionary of headers. func openAPIHeaders() -> OpenAPI.ComponentDictionary - func openAPISecuritySchemes() -> OpenAPI.ComponentDictionary + /// Produces the OpenAPI security scheme components. + /// - Returns: A component dictionary of security schemes. + func openAPISecuritySchemes() + -> OpenAPI.ComponentDictionary + /// Produces the OpenAPI link components. + /// - Returns: A component dictionary of links. func openAPILinks() -> OpenAPI.ComponentDictionary } -public extension ComponentsRepresentable { +extension ComponentsRepresentable { - func openAPISchemas() -> OpenAPI.ComponentDictionary { + /// Default implementation for building schema components. + /// - Returns: A component dictionary of JSON schemas. + public func openAPISchemas() -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in schemas { @@ -41,8 +77,12 @@ public extension ComponentsRepresentable { } return result } - - func openAPIParameters() -> OpenAPI.ComponentDictionary { + + /// Default implementation for building parameter components. + /// - Returns: A component dictionary of parameters. + public func openAPIParameters() + -> OpenAPI.ComponentDictionary + { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in parameters { @@ -53,7 +93,11 @@ public extension ComponentsRepresentable { return result } - func openAPIExamples() -> OpenAPI.ComponentDictionary { + /// Default implementation for building example components. + /// - Returns: A component dictionary of examples. + public func openAPIExamples() + -> OpenAPI.ComponentDictionary + { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in examples { @@ -63,8 +107,12 @@ public extension ComponentsRepresentable { } return result } - - func openAPIResponses() -> OpenAPI.ComponentDictionary { + + /// Default implementation for building response components. + /// - Returns: A component dictionary of responses. + public func openAPIResponses() + -> OpenAPI.ComponentDictionary + { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in responses { @@ -75,7 +123,10 @@ public extension ComponentsRepresentable { return result } - func openAPIRequestBodies() -> OpenAPI.ComponentDictionary + /// Default implementation for building request body components. + /// - Returns: A component dictionary of requests. + public func openAPIRequestBodies() + -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] @@ -87,7 +138,10 @@ public extension ComponentsRepresentable { return result } - func openAPIHeaders() -> OpenAPI.ComponentDictionary { + /// Default implementation for building header components. + /// - Returns: A component dictionary of headers. + public func openAPIHeaders() -> OpenAPI.ComponentDictionary + { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in headers { @@ -98,7 +152,9 @@ public extension ComponentsRepresentable { return result } - func openAPISecuritySchemes() + /// Default implementation for building security scheme components. + /// - Returns: A component dictionary of security schemes. + public func openAPISecuritySchemes() -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] @@ -106,12 +162,15 @@ public extension ComponentsRepresentable { print(securityRequirements) for requirement in securityRequirements { let scheme = requirement.security - result[.init(stringLiteral: scheme.openAPIIdentifier)] = scheme.openAPISecurityScheme() + result[.init(stringLiteral: scheme.openAPIIdentifier)] = + scheme.openAPISecurityScheme() } return result } - func openAPILinks() -> OpenAPI.ComponentDictionary { + /// Default implementation for building link components. + /// - Returns: A component dictionary of links. + public func openAPILinks() -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] for (key, value) in links { @@ -129,7 +188,9 @@ public extension ComponentsRepresentable { // return result // } - func openAPIComponents() -> OpenAPI.Components { + /// Builds an OpenAPI components object. + /// - Returns: The OpenAPI components. + public func openAPIComponents() -> OpenAPI.Components { .init( schemas: openAPISchemas(), responses: openAPIResponses(), diff --git a/Sources/FeatherOpenAPI/Components/OpenAPIComponentsRepresentable.swift b/Sources/FeatherOpenAPI/Components/OpenAPIComponentsRepresentable.swift index b7e3b42..c936cf4 100644 --- a/Sources/FeatherOpenAPI/Components/OpenAPIComponentsRepresentable.swift +++ b/Sources/FeatherOpenAPI/Components/OpenAPIComponentsRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPIComponentsRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,13 +7,17 @@ import OpenAPIKit30 - +/// A type that can produce OpenAPI components. public protocol OpenAPIComponentsRepresentable { + /// Returns the OpenAPI components representation. + /// - Returns: The OpenAPI components. func openAPIComponents() -> OpenAPI.Components } extension OpenAPI.Components: OpenAPIComponentsRepresentable { + /// Returns `self` as OpenAPI components. + /// - Returns: The current components value. public func openAPIComponents() -> OpenAPI.Components { self } diff --git a/Sources/FeatherOpenAPI/Contact/ContactRepresentable.swift b/Sources/FeatherOpenAPI/Contact/ContactRepresentable.swift index c4c1283..c8bffc6 100644 --- a/Sources/FeatherOpenAPI/Contact/ContactRepresentable.swift +++ b/Sources/FeatherOpenAPI/Contact/ContactRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// ContactRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -7,22 +7,31 @@ import OpenAPIKit30 +/// Describes an OpenAPI contact. public protocol ContactRepresentable: OpenAPIContactRepresentable, VendorExtensionsProperty { + /// Contact name. var name: String? { get } + /// Contact URL. var url: LocationRepresentable? { get } + /// Contact email. var email: String? { get } } -public extension ContactRepresentable { - - var name: String? { nil } - var url: LocationRepresentable? { nil } - var email: String? { nil } - - func openAPIContact() -> OpenAPI.Document.Info.Contact { +extension ContactRepresentable { + + /// Default name is `nil`. + public var name: String? { nil } + /// Default URL is `nil`. + public var url: LocationRepresentable? { nil } + /// Default email is `nil`. + public var email: String? { nil } + + /// Builds an OpenAPI contact object. + /// - Returns: The OpenAPI contact. + public func openAPIContact() -> OpenAPI.Document.Info.Contact { .init( name: name, url: url?.openAPILocation(), diff --git a/Sources/FeatherOpenAPI/Contact/OpenAPIContactRepresentable.swift b/Sources/FeatherOpenAPI/Contact/OpenAPIContactRepresentable.swift index 609c1a2..023cf1c 100644 --- a/Sources/FeatherOpenAPI/Contact/OpenAPIContactRepresentable.swift +++ b/Sources/FeatherOpenAPI/Contact/OpenAPIContactRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPIContactRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,13 +7,18 @@ import OpenAPIKit30 +/// A type that can produce OpenAPI contact information. public protocol OpenAPIContactRepresentable { + /// Returns the OpenAPI contact representation. + /// - Returns: The OpenAPI contact. func openAPIContact() -> OpenAPI.Document.Info.Contact } extension OpenAPI.Document.Info.Contact: OpenAPIContactRepresentable { + /// Returns `self` as OpenAPI contact information. + /// - Returns: The current contact value. public func openAPIContact() -> OpenAPI.Document.Info.Contact { self } diff --git a/Sources/FeatherOpenAPI/Content/Content.swift b/Sources/FeatherOpenAPI/Content/Content.swift index 90735bf..54aa6ca 100644 --- a/Sources/FeatherOpenAPI/Content/Content.swift +++ b/Sources/FeatherOpenAPI/Content/Content.swift @@ -1,5 +1,5 @@ // -// File.swift +// Content.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,15 +7,19 @@ import OpenAPIKit30 +/// Concrete content wrapper around a schema. public struct Content: ContentRepresentable { + /// The schema for this content. public var schema: any SchemaRepresentable { _schema } var _schema: T - + + /// Creates content from a schema. + /// - Parameter schema: The schema to expose. public init(_ schema: T) { self._schema = schema } diff --git a/Sources/FeatherOpenAPI/Content/ContentMap.swift b/Sources/FeatherOpenAPI/Content/ContentMap.swift index c07709d..35e03dd 100644 --- a/Sources/FeatherOpenAPI/Content/ContentMap.swift +++ b/Sources/FeatherOpenAPI/Content/ContentMap.swift @@ -1,5 +1,5 @@ // -// File.swift +// ContentMap.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,8 +7,8 @@ import OpenAPIKit30 -public typealias ContentMap = OrderedDictionary -< +/// Ordered map of content types to content definitions. +public typealias ContentMap = OrderedDictionary< OpenAPI.ContentType, ContentRepresentable > diff --git a/Sources/FeatherOpenAPI/Content/ContentRepresentable.swift b/Sources/FeatherOpenAPI/Content/ContentRepresentable.swift index c21b370..a566fd7 100644 --- a/Sources/FeatherOpenAPI/Content/ContentRepresentable.swift +++ b/Sources/FeatherOpenAPI/Content/ContentRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// ContentRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,17 +7,21 @@ import OpenAPIKit30 +/// Describes OpenAPI content with a schema. public protocol ContentRepresentable: OpenAPIContentRepresentable, ReferencedSchemaMapRepresentable, VendorExtensionsProperty { + /// The schema for the content. var schema: SchemaRepresentable { get } } -public extension ContentRepresentable { +extension ContentRepresentable { - func openAPIContent() -> OpenAPI.Content { + /// Builds an OpenAPI content object. + /// - Returns: The OpenAPI content. + public func openAPIContent() -> OpenAPI.Content { .init( schema: schema.openAPISchema(), examples: nil, @@ -25,8 +29,11 @@ public extension ContentRepresentable { vendorExtensions: vendorExtensions ) } - - var referencedSchemaMap: OrderedDictionary { + + /// Referenced schemas for this content. + public var referencedSchemaMap: + OrderedDictionary + { schema.allReferencedSchemaMap() } } diff --git a/Sources/FeatherOpenAPI/Content/OpenAPIContentRepresentable.swift b/Sources/FeatherOpenAPI/Content/OpenAPIContentRepresentable.swift index e2cd692..4e2d695 100644 --- a/Sources/FeatherOpenAPI/Content/OpenAPIContentRepresentable.swift +++ b/Sources/FeatherOpenAPI/Content/OpenAPIContentRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPIContentRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -7,19 +7,20 @@ import OpenAPIKit30 +/// A type that can produce OpenAPI content. public protocol OpenAPIContentRepresentable { + /// Returns the OpenAPI content representation. + /// - Returns: The OpenAPI content. func openAPIContent() -> OpenAPI.Content } extension OpenAPI.Content: OpenAPIContentRepresentable { + /// Returns `self` as OpenAPI content. + /// - Returns: The current content value. public func openAPIContent() -> OpenAPI.Content { self } } // MARK: - - - - - diff --git a/Sources/FeatherOpenAPI/Document/DocumentRepresentable.swift b/Sources/FeatherOpenAPI/Document/DocumentRepresentable.swift index a1919c8..c75bd71 100644 --- a/Sources/FeatherOpenAPI/Document/DocumentRepresentable.swift +++ b/Sources/FeatherOpenAPI/Document/DocumentRepresentable.swift @@ -1,5 +1,5 @@ // -// File 2.swift +// DocumentRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -7,42 +7,59 @@ import OpenAPIKit30 +/// Describes a high-level OpenAPI document with standard defaults. public protocol DocumentRepresentable: OpenAPIDocumentRepresentable, VendorExtensionsProperty, ReferencedTagMapRepresentable, ReferencedSecuritySchemeMapRepresentable { + /// The document information metadata. var info: OpenAPIInfoRepresentable { get } + /// The list of servers where the API is served. var servers: [OpenAPIServerRepresentable] { get } + /// The map of path items by path. var paths: PathMap { get } + /// The reusable component definitions. var components: OpenAPIComponentsRepresentable { get } + /// External documentation for this API, if any. var externalDocs: ExternalDocsRepresentable? { get } } -public extension DocumentRepresentable { +extension DocumentRepresentable { - var servers: [OpenAPIServerRepresentable] { [] } - var paths: PathMap { [:] } + /// Default servers for the document. + public var servers: [OpenAPIServerRepresentable] { [] } + /// Default empty path map. + public var paths: PathMap { [:] } - var externalDocs: ExternalDocsRepresentable? { nil } + /// Default external docs is `nil`. + public var externalDocs: ExternalDocsRepresentable? { nil } - var referencedTags: [OpenAPITagRepresentable] { + /// Collects all tags referenced by the document. + public var referencedTags: [OpenAPITagRepresentable] { paths.values.map { $0.referencedTags }.flatMap { $0 } } - var referencedSecurityRequirements: [SecurityRequirementRepresentable] { + /// Collects all security requirements referenced by the document. + public var referencedSecurityRequirements: + [SecurityRequirementRepresentable] + { paths.values.map { $0.referencedSecurityRequirements }.flatMap { $0 } } - - func openAPIDocument() -> OpenAPI.Document { + + /// Builds an OpenAPI document from the representable values. + /// - Returns: A concrete OpenAPI document. + public func openAPIDocument() -> OpenAPI.Document { .init( openAPIVersion: .v3_0_0, info: info.openAPIInfo(), servers: servers.map { $0.openAPIServer() }, paths: paths.mapValues { .init($0.openAPIPathItem()) }, components: components.openAPIComponents(), - security: referencedSecurityRequirements.map { $0.openAPISecurityRequirement() }, + security: referencedSecurityRequirements.map { + $0.openAPISecurityRequirement() + }, tags: referencedTags.map { $0.openAPITag() }, externalDocs: externalDocs?.openAPIExternalDocs(), vendorExtensions: vendorExtensions diff --git a/Sources/FeatherOpenAPI/Document/OpenAPIDocumentRepresentable.swift b/Sources/FeatherOpenAPI/Document/OpenAPIDocumentRepresentable.swift index c66eb54..1c0116e 100644 --- a/Sources/FeatherOpenAPI/Document/OpenAPIDocumentRepresentable.swift +++ b/Sources/FeatherOpenAPI/Document/OpenAPIDocumentRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPIDocumentRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,12 +7,17 @@ import OpenAPIKit30 +/// A type that can produce an OpenAPI document. public protocol OpenAPIDocumentRepresentable { + /// Returns the OpenAPI document representation. + /// - Returns: The OpenAPI document. func openAPIDocument() -> OpenAPI.Document } extension OpenAPI.Document: OpenAPIDocumentRepresentable { + /// Returns `self` as an OpenAPI document. + /// - Returns: The current document. public func openAPIDocument() -> OpenAPI.Document { self } diff --git a/Sources/FeatherOpenAPI/Example/ExampleID.swift b/Sources/FeatherOpenAPI/Example/ExampleID.swift index 11bed6b..e7ebc26 100644 --- a/Sources/FeatherOpenAPI/Example/ExampleID.swift +++ b/Sources/FeatherOpenAPI/Example/ExampleID.swift @@ -1,14 +1,18 @@ // -// File.swift +// ExampleID.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. // +/// Strongly typed identifier for reusable OpenAPI examples. public struct ExampleID: Sendable, Equatable, Hashable, Codable { + /// The raw identifier value. public var rawValue: String + /// Creates an example identifier. + /// - Parameter rawValue: The raw identifier value. public init( _ rawValue: String ) { diff --git a/Sources/FeatherOpenAPI/Example/ExampleRepresentable.swift b/Sources/FeatherOpenAPI/Example/ExampleRepresentable.swift index a0ff853..94a7a7e 100644 --- a/Sources/FeatherOpenAPI/Example/ExampleRepresentable.swift +++ b/Sources/FeatherOpenAPI/Example/ExampleRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// ExampleRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -7,23 +7,32 @@ import OpenAPIKit30 +/// Describes an OpenAPI example with defaults. public protocol ExampleRepresentable: OpenAPIExampleRepresentable, Identifiable, DescriptionProperty, VendorExtensionsProperty { + /// Short summary of the example. var summary: String? { get } + /// Example payload value. var value: AnyCodable { get } } -public extension ExampleRepresentable { +extension ExampleRepresentable { - func reference() -> ExampleReference { + /// Creates a reference wrapper for this example. + /// - Returns: An example reference. + public func reference() -> ExampleReference { .init(self) } - - func openAPIExample() -> Either, OpenAPI.Example> { + + /// Builds an OpenAPI example object or reference. + /// - Returns: The OpenAPI example representation. + public func openAPIExample() -> Either< + JSONReference, OpenAPI.Example + > { .init( .init( summary: summary, diff --git a/Sources/FeatherOpenAPI/Example/OpenAPIExampleRepresentable.swift b/Sources/FeatherOpenAPI/Example/OpenAPIExampleRepresentable.swift index 5c30211..ca8449a 100644 --- a/Sources/FeatherOpenAPI/Example/OpenAPIExampleRepresentable.swift +++ b/Sources/FeatherOpenAPI/Example/OpenAPIExampleRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPIExampleRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,13 +7,22 @@ import OpenAPIKit30 +/// A type that can produce an OpenAPI example or reference. public protocol OpenAPIExampleRepresentable { - func openAPIExample() -> Either, OpenAPI.Example> + /// Returns the OpenAPI example representation. + /// - Returns: An OpenAPI example or reference. + func openAPIExample() -> Either< + JSONReference, OpenAPI.Example + > } extension OpenAPI.Example: OpenAPIExampleRepresentable { - public func openAPIExample() -> Either, OpenAPI.Example> { + /// Returns `self` wrapped as an OpenAPI example. + /// - Returns: An OpenAPI example value. + public func openAPIExample() -> Either< + JSONReference, OpenAPI.Example + > { .init(self) } } diff --git a/Sources/FeatherOpenAPI/ExternalDocs/ExternalDocsRepresentable.swift b/Sources/FeatherOpenAPI/ExternalDocs/ExternalDocsRepresentable.swift index f758510..3100e5a 100644 --- a/Sources/FeatherOpenAPI/ExternalDocs/ExternalDocsRepresentable.swift +++ b/Sources/FeatherOpenAPI/ExternalDocs/ExternalDocsRepresentable.swift @@ -1,24 +1,27 @@ // -// File.swift +// ExternalDocsRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. // - import OpenAPIKit30 +/// Describes external documentation for the API. public protocol ExternalDocsRepresentable: OpenAPIExternalDocsRepresentable, DescriptionProperty, VendorExtensionsProperty { + /// The external documentation URL. var url: LocationRepresentable { get } } -public extension ExternalDocsRepresentable { +extension ExternalDocsRepresentable { - func openAPIExternalDocs() -> OpenAPI.ExternalDocumentation { + /// Builds an OpenAPI external documentation object. + /// - Returns: The OpenAPI external documentation. + public func openAPIExternalDocs() -> OpenAPI.ExternalDocumentation { .init( description: description, url: url.openAPILocation(), diff --git a/Sources/FeatherOpenAPI/ExternalDocs/OpenAPIExternalDocsRepresentable.swift b/Sources/FeatherOpenAPI/ExternalDocs/OpenAPIExternalDocsRepresentable.swift index 95aeb3b..ce9e187 100644 --- a/Sources/FeatherOpenAPI/ExternalDocs/OpenAPIExternalDocsRepresentable.swift +++ b/Sources/FeatherOpenAPI/ExternalDocs/OpenAPIExternalDocsRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPIExternalDocsRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,14 +7,18 @@ import OpenAPIKit30 +/// A type that can produce OpenAPI external documentation. public protocol OpenAPIExternalDocsRepresentable { + /// Returns the OpenAPI external documentation. + /// - Returns: The external documentation. func openAPIExternalDocs() -> OpenAPI.ExternalDocumentation } extension OpenAPI.ExternalDocumentation: OpenAPIExternalDocsRepresentable { + /// Returns `self` as OpenAPI external documentation. + /// - Returns: The current external documentation value. public func openAPIExternalDocs() -> OpenAPI.ExternalDocumentation { self } } - diff --git a/Sources/FeatherOpenAPI/Header/HeaderID.swift b/Sources/FeatherOpenAPI/Header/HeaderID.swift index 198b931..159b50e 100644 --- a/Sources/FeatherOpenAPI/Header/HeaderID.swift +++ b/Sources/FeatherOpenAPI/Header/HeaderID.swift @@ -1,14 +1,18 @@ // -// File.swift +// HeaderID.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. // +/// Strongly typed identifier for reusable OpenAPI headers. public struct HeaderID: Sendable, Equatable, Hashable, Codable { + /// The raw identifier value. public var rawValue: String + /// Creates a header identifier. + /// - Parameter rawValue: The raw identifier value. public init( _ rawValue: String ) { diff --git a/Sources/FeatherOpenAPI/Header/HeaderMap.swift b/Sources/FeatherOpenAPI/Header/HeaderMap.swift index db2ebcf..9cdefc6 100644 --- a/Sources/FeatherOpenAPI/Header/HeaderMap.swift +++ b/Sources/FeatherOpenAPI/Header/HeaderMap.swift @@ -1,5 +1,5 @@ // -// File.swift +// HeaderMap.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,9 +7,8 @@ import OpenAPIKit30 -public typealias HeaderMap = OrderedDictionary -< +/// Ordered map of header names to header definitions. +public typealias HeaderMap = OrderedDictionary< String, HeaderRepresentable > - diff --git a/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift b/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift index ee91b4f..2b082e6 100644 --- a/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift +++ b/Sources/FeatherOpenAPI/Header/HeaderRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// HeaderRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -7,6 +7,7 @@ import OpenAPIKit30 +/// Describes an OpenAPI header with defaults. public protocol HeaderRepresentable: OpenAPIHeaderRepresentable, Identifiable, @@ -17,16 +18,23 @@ public protocol HeaderRepresentable: // reference ReferencedSchemaMapRepresentable { + /// The schema describing the header value. var schema: OpenAPISchemaRepresentable { get } } -public extension HeaderRepresentable { - - func reference() -> HeaderReference { +extension HeaderRepresentable { + + /// Creates a reference wrapper for this header. + /// - Returns: A header reference. + public func reference() -> HeaderReference { .init(self) } - func openAPIHeader() -> Either, OpenAPI.Header> { + /// Builds an OpenAPI header object or reference. + /// - Returns: The OpenAPI header representation. + public func openAPIHeader() -> Either< + JSONReference, OpenAPI.Header + > { .init( .init( schema: schema.openAPISchema(), @@ -37,8 +45,11 @@ public extension HeaderRepresentable { ) ) } - - var referencedSchemaMap: OrderedDictionary { + + /// Referenced schemas used by the header. + public var referencedSchemaMap: + OrderedDictionary + { guard let schema = schema as? SchemaRepresentable else { return [:] } diff --git a/Sources/FeatherOpenAPI/Header/OpenAPIHeaderRepresentable.swift b/Sources/FeatherOpenAPI/Header/OpenAPIHeaderRepresentable.swift index 7fa567d..3557b30 100644 --- a/Sources/FeatherOpenAPI/Header/OpenAPIHeaderRepresentable.swift +++ b/Sources/FeatherOpenAPI/Header/OpenAPIHeaderRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPIHeaderRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,13 +7,22 @@ import OpenAPIKit30 +/// A type that can produce an OpenAPI header or reference. public protocol OpenAPIHeaderRepresentable { - func openAPIHeader() -> Either, OpenAPI.Header> + /// Returns the OpenAPI header representation. + /// - Returns: An OpenAPI header or reference. + func openAPIHeader() -> Either< + JSONReference, OpenAPI.Header + > } extension OpenAPI.Header: OpenAPIHeaderRepresentable { - public func openAPIHeader() -> Either, OpenAPI.Header> { + /// Returns `self` wrapped as an OpenAPI header. + /// - Returns: An OpenAPI header value. + public func openAPIHeader() -> Either< + JSONReference, OpenAPI.Header + > { .init(self) } } diff --git a/Sources/FeatherOpenAPI/Identifiable.swift b/Sources/FeatherOpenAPI/Identifiable.swift index f988d00..c8983e9 100644 --- a/Sources/FeatherOpenAPI/Identifiable.swift +++ b/Sources/FeatherOpenAPI/Identifiable.swift @@ -1,23 +1,27 @@ // -// File.swift +// Identifiable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. // +/// Provides a default OpenAPI identifier based on the type name. public protocol Identifiable: Sendable { + /// The identifier to use for OpenAPI component references. var openAPIIdentifier: String { get } } -public extension Identifiable { +extension Identifiable { - var openAPIIdentifier: String { + /// Default identifier derived from the type name. + public var openAPIIdentifier: String { let name = String(reflecting: type(of: self)) var components = name.split(separator: ".") if components.count > 1 { components.remove(at: 0) // remove namespace if present } - let identifier = components + let identifier = + components .joined(separator: "") .replacing("()", with: "") .replacing("GenericComponent", with: "Generic") @@ -25,8 +29,3 @@ public extension Identifiable { return identifier } } - - - - - diff --git a/Sources/FeatherOpenAPI/Info/InfoRepresentable.swift b/Sources/FeatherOpenAPI/Info/InfoRepresentable.swift index 5a6b935..e0e32dc 100644 --- a/Sources/FeatherOpenAPI/Info/InfoRepresentable.swift +++ b/Sources/FeatherOpenAPI/Info/InfoRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// InfoRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,25 +7,36 @@ import OpenAPIKit30 +/// Describes the OpenAPI document info section. public protocol InfoRepresentable: OpenAPIInfoRepresentable, DescriptionProperty, VendorExtensionsProperty { + /// Display title of the API. var title: String { get } + /// Terms of service URL. var termsOfService: LocationRepresentable? { get } + /// Contact information for the API. var contact: OpenAPIContactRepresentable? { get } + /// License information for the API. var license: OpenAPILicenseRepresentable? { get } + /// Version string for the API. var version: String { get } } -public extension InfoRepresentable { +extension InfoRepresentable { - var termsOfService: LocationRepresentable? { nil } - var contact: OpenAPIContactRepresentable? { nil } - var license: OpenAPILicenseRepresentable? { nil } + /// Default terms of service is `nil`. + public var termsOfService: LocationRepresentable? { nil } + /// Default contact is `nil`. + public var contact: OpenAPIContactRepresentable? { nil } + /// Default license is `nil`. + public var license: OpenAPILicenseRepresentable? { nil } - func openAPIInfo() -> OpenAPI.Document.Info { + /// Builds an OpenAPI document info object. + /// - Returns: The OpenAPI info. + public func openAPIInfo() -> OpenAPI.Document.Info { .init( title: title, description: description, diff --git a/Sources/FeatherOpenAPI/Info/OpenAPIInfoRepresentable.swift b/Sources/FeatherOpenAPI/Info/OpenAPIInfoRepresentable.swift index cb4604b..4ec2e9f 100644 --- a/Sources/FeatherOpenAPI/Info/OpenAPIInfoRepresentable.swift +++ b/Sources/FeatherOpenAPI/Info/OpenAPIInfoRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPIInfoRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -7,13 +7,18 @@ import OpenAPIKit30 +/// A type that can produce OpenAPI document info. public protocol OpenAPIInfoRepresentable { + /// Returns the OpenAPI document info representation. + /// - Returns: The OpenAPI info. func openAPIInfo() -> OpenAPI.Document.Info } extension OpenAPI.Document.Info: OpenAPIInfoRepresentable { + /// Returns `self` as OpenAPI document info. + /// - Returns: The current info value. public func openAPIInfo() -> OpenAPI.Document.Info { self } diff --git a/Sources/FeatherOpenAPI/License/LicenseRepresentable.swift b/Sources/FeatherOpenAPI/License/LicenseRepresentable.swift index e1f7026..5c65884 100644 --- a/Sources/FeatherOpenAPI/License/LicenseRepresentable.swift +++ b/Sources/FeatherOpenAPI/License/LicenseRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// LicenseRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -7,16 +7,21 @@ import OpenAPIKit30 +/// Describes an OpenAPI license. public protocol LicenseRepresentable: OpenAPILicenseRepresentable, VendorExtensionsProperty { + /// License name. var name: String { get } + /// License URL. var url: LocationRepresentable? { get } } extension LicenseRepresentable { + /// Builds an OpenAPI license object. + /// - Returns: The OpenAPI license. public func openAPILicense() -> OpenAPI.Document.Info.License { .init( name: name, diff --git a/Sources/FeatherOpenAPI/License/OpenAPILicenseRepresentable.swift b/Sources/FeatherOpenAPI/License/OpenAPILicenseRepresentable.swift index 25bcd00..b44fb37 100644 --- a/Sources/FeatherOpenAPI/License/OpenAPILicenseRepresentable.swift +++ b/Sources/FeatherOpenAPI/License/OpenAPILicenseRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPILicenseRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,12 +7,17 @@ import OpenAPIKit30 +/// A type that can produce OpenAPI license information. public protocol OpenAPILicenseRepresentable { + /// Returns the OpenAPI license representation. + /// - Returns: The OpenAPI license. func openAPILicense() -> OpenAPI.Document.Info.License } extension OpenAPI.Document.Info.License: OpenAPILicenseRepresentable { + /// Returns `self` as OpenAPI license information. + /// - Returns: The current license value. public func openAPILicense() -> OpenAPI.Document.Info.License { self } diff --git a/Sources/FeatherOpenAPI/Link/LinkID.swift b/Sources/FeatherOpenAPI/Link/LinkID.swift index b8d1c9e..11c0b25 100644 --- a/Sources/FeatherOpenAPI/Link/LinkID.swift +++ b/Sources/FeatherOpenAPI/Link/LinkID.swift @@ -1,14 +1,18 @@ // -// File.swift +// LinkID.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. // +/// Strongly typed identifier for reusable OpenAPI links. public struct LinkID: Sendable, Equatable, Hashable, Codable { + /// The raw identifier value. public var rawValue: String + /// Creates a link identifier. + /// - Parameter rawValue: The raw identifier value. public init( _ rawValue: String ) { diff --git a/Sources/FeatherOpenAPI/Link/OpenAPILinkRepresentable.swift b/Sources/FeatherOpenAPI/Link/OpenAPILinkRepresentable.swift index 178d73b..d7bd9d5 100644 --- a/Sources/FeatherOpenAPI/Link/OpenAPILinkRepresentable.swift +++ b/Sources/FeatherOpenAPI/Link/OpenAPILinkRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPILinkRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,12 +7,17 @@ import OpenAPIKit30 +/// A type that can produce an OpenAPI link. public protocol OpenAPILinkRepresentable { + /// Returns the OpenAPI link representation. + /// - Returns: The OpenAPI link. func openAPILink() -> OpenAPI.Link } extension OpenAPI.Link: OpenAPILinkRepresentable { + /// Returns `self` as an OpenAPI link. + /// - Returns: The current link value. public func openAPILink() -> OpenAPI.Link { self } diff --git a/Sources/FeatherOpenAPI/Location/LocationRepresentable.swift b/Sources/FeatherOpenAPI/Location/LocationRepresentable.swift index 74206e7..58e6759 100644 --- a/Sources/FeatherOpenAPI/Location/LocationRepresentable.swift +++ b/Sources/FeatherOpenAPI/Location/LocationRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// LocationRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -11,13 +11,17 @@ import FoundationEssentials import Foundation #endif +/// A type that can provide a URL location string. public protocol LocationRepresentable: OpenAPILocationRepresentable { + /// The URL string for the location. var location: String { get } } -public extension LocationRepresentable { +extension LocationRepresentable { - func openAPILocation() -> URL { + /// Converts the location string into a `URL`. + /// - Returns: A URL created from the location string. + public func openAPILocation() -> URL { .init(string: location)! } } @@ -25,4 +29,3 @@ public extension LocationRepresentable { //extension String: URLRepresentable { // public var rawURL: String { self } //} - diff --git a/Sources/FeatherOpenAPI/Location/OpenAPILocationRepresentable.swift b/Sources/FeatherOpenAPI/Location/OpenAPILocationRepresentable.swift index e1d9080..213e165 100644 --- a/Sources/FeatherOpenAPI/Location/OpenAPILocationRepresentable.swift +++ b/Sources/FeatherOpenAPI/Location/OpenAPILocationRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPILocationRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -11,12 +11,17 @@ import FoundationEssentials import Foundation #endif +/// A type that can produce a URL location. public protocol OpenAPILocationRepresentable { + /// Returns the URL representation. + /// - Returns: The URL. func openAPILocation() -> URL } extension URL: OpenAPILocationRepresentable { + /// Returns `self` as a URL location. + /// - Returns: The current URL. public func openAPILocation() -> URL { self } diff --git a/Sources/FeatherOpenAPI/Operation/OpenAPIOperationRepresentable.swift b/Sources/FeatherOpenAPI/Operation/OpenAPIOperationRepresentable.swift index dd7f789..9aebb06 100644 --- a/Sources/FeatherOpenAPI/Operation/OpenAPIOperationRepresentable.swift +++ b/Sources/FeatherOpenAPI/Operation/OpenAPIOperationRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPIOperationRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,12 +7,17 @@ import OpenAPIKit30 +/// A type that can produce an OpenAPI operation. public protocol OpenAPIOperationRepresentable { + /// Returns the OpenAPI operation representation. + /// - Returns: The OpenAPI operation. func openAPIOperation() -> OpenAPI.Operation } extension OpenAPI.Operation: OpenAPIOperationRepresentable { + /// Returns `self` as an OpenAPI operation. + /// - Returns: The current operation value. public func openAPIOperation() -> OpenAPI.Operation { self } diff --git a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift index 4d0e405..66ac92f 100644 --- a/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift +++ b/Sources/FeatherOpenAPI/Operation/OperationRepresentable.swift @@ -1,5 +1,5 @@ // -// File 2.swift +// OperationRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -7,9 +7,9 @@ import OpenAPIKit30 -fileprivate extension String { +extension String { - func lowercasedFirstLetter() -> String { + fileprivate func lowercasedFirstLetter() -> String { guard !isEmpty else { return self } @@ -17,7 +17,7 @@ fileprivate extension String { } } - +/// Describes an OpenAPI operation with defaults and reference aggregation. public protocol OperationRepresentable: OpenAPIOperationRepresentable, // properties @@ -33,29 +33,42 @@ public protocol OperationRepresentable: ReferencedTagMapRepresentable, ReferencedSecuritySchemeMapRepresentable { -// associatedtype RequestBodyType: RequestBodyRepresentable - + // associatedtype RequestBodyType: RequestBodyRepresentable + + /// Tags associated with the operation. var tags: [TagRepresentable] { get } + /// Short summary of the operation. var summary: String? { get } + /// Optional explicit operation identifier. var operationId: String? { get } + /// Parameters accepted by the operation. var parameters: [ParameterRepresentable] { get } + /// Optional request body. var requestBody: RequestBodyRepresentable? { get } + /// Response map keyed by status code. var responseMap: ResponseMap { get } + /// Optional security requirements. var security: [SecurityRequirementRepresentable]? { get } + /// Optional per-operation servers. var servers: [ServerRepresentable]? { get } } -public extension OperationRepresentable { +extension OperationRepresentable { + + /// Default tags are empty. + public var tags: [TagRepresentable] { [] } + /// Default summary is `nil`. + public var summary: String? { nil } + + /// Default operation identifier is `nil`. + public var operationId: String? { nil } + /// Default parameters are empty. + public var parameters: [ParameterRepresentable] { [] } - var tags: [TagRepresentable] { [] } - var summary: String? { nil } - - var operationId: String? { nil } - var parameters: [ParameterRepresentable] { [] } - - static var operationId: String { + /// Computes a default operation identifier from the type name. + public static var operationId: String { var components = String(reflecting: self) .split(separator: ".") .dropFirst() @@ -68,15 +81,17 @@ public extension OperationRepresentable { return components.joined(separator: "") } + /// Default request body is `nil`. + public var requestBody: RequestBodyRepresentable? { nil } + /// Default security requirements are `nil`. + public var security: [SecurityRequirementRepresentable]? { nil } + /// Default servers list is `nil`. + public var servers: [ServerRepresentable]? { nil } - var requestBody: RequestBodyRepresentable? { nil } - var security: [SecurityRequirementRepresentable]? { nil } - var servers: [ServerRepresentable]? { nil } - private var openAPITags: [String]? { tags.isEmpty ? nil : tags.map { $0.name } } - + private var openAPISecurityRequirements: [OpenAPI.SecurityRequirement]? { guard let security, !security.isEmpty else { return nil @@ -84,8 +99,10 @@ public extension OperationRepresentable { return security.map { $0.openAPISecurityRequirement() } } - - func openAPIOperation() -> OpenAPI.Operation { + + /// Builds an OpenAPI operation. + /// - Returns: The OpenAPI operation. + public func openAPIOperation() -> OpenAPI.Operation { if let requestBody { return .init( tags: openAPITags, @@ -118,16 +135,19 @@ public extension OperationRepresentable { vendorExtensions: vendorExtensions ) } - + // MARK: - refs - - var referencedSchemaMap: OrderedDictionary { + + /// Aggregated referenced schemas from parameters, request body, and responses. + public var referencedSchemaMap: + OrderedDictionary + { var results = OrderedDictionary() - + for parameter in parameters { results.merge(parameter.referencedSchemaMap) } - + if let schemaMap = requestBody?.referencedSchemaMap { results.merge(schemaMap) } @@ -135,11 +155,11 @@ public extension OperationRepresentable { let headers = responseMap.values .map { $0.headerMap.values } .flatMap { $0 } - + for header in headers { results.merge(header.referencedSchemaMap) } - + let contents = responseMap.values .map { $0.contentMap.values } .flatMap { $0 } @@ -150,13 +170,18 @@ public extension OperationRepresentable { return results } - - var referencedParameterMap: OrderedDictionary { - var results = OrderedDictionary() - + + /// Aggregated referenced parameters used by the operation. + public var referencedParameterMap: + OrderedDictionary + { + var results = OrderedDictionary< + ParameterID, OpenAPIParameterRepresentable + >() + for parameter in parameters { if let ref = parameter as? ParameterReferenceRepresentable { - if case let .b(parameter) = ref.object.openAPIParameter() { + if case .b(let parameter) = ref.object.openAPIParameter() { results[ref.id] = parameter } } @@ -164,8 +189,13 @@ public extension OperationRepresentable { return results } - var referencedRequestBodyMap: OrderedDictionary { - var results = OrderedDictionary() + /// Aggregated referenced request bodies used by the operation. + public var referencedRequestBodyMap: + OrderedDictionary + { + var results = OrderedDictionary< + RequestBodyID, OpenAPIRequestBodyRepresentable + >() if let ref = requestBody as? RequestBodyReferenceRepresentable { results[ref.id] = ref.object @@ -173,7 +203,10 @@ public extension OperationRepresentable { return results } - var referencedHeaderMap: OrderedDictionary { + /// Aggregated referenced headers used by responses. + public var referencedHeaderMap: + OrderedDictionary + { var results = OrderedDictionary() let headers = responseMap.values @@ -182,7 +215,7 @@ public extension OperationRepresentable { for header in headers { if let ref = header as? HeaderReferenceRepresentable { - if case let .b(header) = ref.object.openAPIHeader() { + if case .b(let header) = ref.object.openAPIHeader() { results[ref.id] = header } } @@ -190,12 +223,17 @@ public extension OperationRepresentable { return results } - var referencedResponseMap: OrderedDictionary { - var results = OrderedDictionary() + /// Aggregated referenced responses used by the operation. + public var referencedResponseMap: + OrderedDictionary + { + var results = OrderedDictionary< + ResponseID, OpenAPIResponseRepresentable + >() for response in responseMap.values { if let ref = response as? ResponseReferenceRepresentable { - if case let .b(response) = ref.object.openAPIResponse() { + if case .b(let response) = ref.object.openAPIResponse() { results[ref.id] = response } } @@ -203,11 +241,15 @@ public extension OperationRepresentable { return results } - var referencedTags: [OpenAPITagRepresentable] { + /// Referenced tags for the operation. + public var referencedTags: [OpenAPITagRepresentable] { tags } - - var referencedSecurityRequirements: [SecurityRequirementRepresentable] { + + /// Referenced security requirements for the operation. + public var referencedSecurityRequirements: + [SecurityRequirementRepresentable] + { security?.map { $0 } ?? [] } } diff --git a/Sources/FeatherOpenAPI/OrderedDictionary+Merge.swift b/Sources/FeatherOpenAPI/OrderedDictionary+Merge.swift index cdab2b5..0eef265 100644 --- a/Sources/FeatherOpenAPI/OrderedDictionary+Merge.swift +++ b/Sources/FeatherOpenAPI/OrderedDictionary+Merge.swift @@ -1,3 +1,9 @@ +// +// OrderedDictionary+Merge.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // @@ -8,7 +14,7 @@ import OpenAPIKit30 extension OrderedDictionary { - + mutating func merge(_ other: Self) { merge(other, uniquingKeysWith: { _, new in new }) } diff --git a/Sources/FeatherOpenAPI/Parameter/Abstraction/OpenAPIParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/Abstraction/OpenAPIParameterRepresentable.swift index 602cc30..f4ac8d5 100644 --- a/Sources/FeatherOpenAPI/Parameter/Abstraction/OpenAPIParameterRepresentable.swift +++ b/Sources/FeatherOpenAPI/Parameter/Abstraction/OpenAPIParameterRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPIParameterRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,13 +7,22 @@ import OpenAPIKit30 +/// A type that can produce an OpenAPI parameter or reference. public protocol OpenAPIParameterRepresentable { - func openAPIParameter() -> Either, OpenAPI.Parameter> + /// Returns the OpenAPI parameter representation. + /// - Returns: An OpenAPI parameter or reference. + func openAPIParameter() -> Either< + JSONReference, OpenAPI.Parameter + > } extension OpenAPI.Parameter: OpenAPIParameterRepresentable { - - public func openAPIParameter() -> Either, OpenAPI.Parameter> { + + /// Returns `self` wrapped as an OpenAPI parameter. + /// - Returns: An OpenAPI parameter value. + public func openAPIParameter() -> Either< + JSONReference, OpenAPI.Parameter + > { .init(self) } } diff --git a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift index 12f3e32..8d41ade 100644 --- a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift +++ b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift @@ -1,14 +1,18 @@ // -// File.swift +// ParameterID.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. // +/// Strongly typed identifier for reusable OpenAPI parameters. public struct ParameterID: Sendable, Equatable, Hashable, Codable { + /// The raw identifier value. public var rawValue: String + /// Creates a parameter identifier. + /// - Parameter rawValue: The raw identifier value. public init( _ rawValue: String ) { diff --git a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift index 9f0b176..d381ecd 100644 --- a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift +++ b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// ParameterRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -7,6 +7,7 @@ import OpenAPIKit30 +/// Describes an OpenAPI parameter with defaults. public protocol ParameterRepresentable: OpenAPIParameterRepresentable, Identifiable, @@ -17,19 +18,28 @@ public protocol ParameterRepresentable: // reference ReferencedSchemaMapRepresentable { + /// The parameter name. var name: String { get } + /// The parameter context (path, query, header, cookie). var context: OpenAPI.Parameter.Context { get } + /// The schema describing the parameter value. var schema: OpenAPISchemaRepresentable { get } - + } -public extension ParameterRepresentable { - - func reference() -> ParameterReference { +extension ParameterRepresentable { + + /// Creates a reference wrapper for this parameter. + /// - Returns: A parameter reference. + public func reference() -> ParameterReference { .init(self) } - - func openAPIParameter() -> Either, OpenAPI.Parameter> { + + /// Builds an OpenAPI parameter object or reference. + /// - Returns: The OpenAPI parameter representation. + public func openAPIParameter() -> Either< + JSONReference, OpenAPI.Parameter + > { .init( .init( name: name, @@ -41,8 +51,11 @@ public extension ParameterRepresentable { ) ) } - - var referencedSchemaMap: OrderedDictionary { + + /// Referenced schemas used by the parameter. + public var referencedSchemaMap: + OrderedDictionary + { guard let schema = schema as? SchemaRepresentable else { return [:] } diff --git a/Sources/FeatherOpenAPI/Parameter/CookieParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/CookieParameterRepresentable.swift index 5a77b85..0d44959 100644 --- a/Sources/FeatherOpenAPI/Parameter/CookieParameterRepresentable.swift +++ b/Sources/FeatherOpenAPI/Parameter/CookieParameterRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// CookieParameterRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,6 +7,7 @@ import OpenAPIKit30 +/// Parameter located in a cookie. public protocol CookieParameterRepresentable: ParameterRepresentable, RequiredProperty @@ -14,9 +15,10 @@ public protocol CookieParameterRepresentable: } -public extension CookieParameterRepresentable { - - var context: OpenAPI.Parameter.Context { +extension CookieParameterRepresentable { + + /// Cookie parameter context. + public var context: OpenAPI.Parameter.Context { .cookie(required: `required`) } } diff --git a/Sources/FeatherOpenAPI/Parameter/HeaderParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/HeaderParameterRepresentable.swift index 2c93713..e600dbe 100644 --- a/Sources/FeatherOpenAPI/Parameter/HeaderParameterRepresentable.swift +++ b/Sources/FeatherOpenAPI/Parameter/HeaderParameterRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// HeaderParameterRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,16 +7,18 @@ import OpenAPIKit30 +/// Parameter located in a header. public protocol HeaderParameterRepresentable: ParameterRepresentable, RequiredProperty { - + } -public extension HeaderParameterRepresentable { - - var context: OpenAPI.Parameter.Context { +extension HeaderParameterRepresentable { + + /// Header parameter context. + public var context: OpenAPI.Parameter.Context { .header(required: `required`) } } diff --git a/Sources/FeatherOpenAPI/Parameter/PathParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/PathParameterRepresentable.swift index bb1d9f3..764bb54 100644 --- a/Sources/FeatherOpenAPI/Parameter/PathParameterRepresentable.swift +++ b/Sources/FeatherOpenAPI/Parameter/PathParameterRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// PathParameterRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,11 +7,13 @@ import OpenAPIKit30 +/// Parameter located in the path. public protocol PathParameterRepresentable: ParameterRepresentable { - + } -public extension PathParameterRepresentable { +extension PathParameterRepresentable { - var context: OpenAPI.Parameter.Context { .path } + /// Path parameter context. + public var context: OpenAPI.Parameter.Context { .path } } diff --git a/Sources/FeatherOpenAPI/Parameter/QueryParameterRepresentable.swift b/Sources/FeatherOpenAPI/Parameter/QueryParameterRepresentable.swift index 69b906e..9447b18 100644 --- a/Sources/FeatherOpenAPI/Parameter/QueryParameterRepresentable.swift +++ b/Sources/FeatherOpenAPI/Parameter/QueryParameterRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// QueryParameterRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,18 +7,22 @@ import OpenAPIKit30 +/// Parameter located in the query string. public protocol QueryParameterRepresentable: ParameterRepresentable, RequiredProperty { + /// Whether empty values are allowed. var allowEmptyValue: Bool { get } } -public extension QueryParameterRepresentable { +extension QueryParameterRepresentable { - var allowEmptyValue: Bool { true } - - var context: OpenAPI.Parameter.Context { + /// Default empty value allowance is `true`. + public var allowEmptyValue: Bool { true } + + /// Query parameter context. + public var context: OpenAPI.Parameter.Context { .query( required: `required`, allowEmptyValue: allowEmptyValue diff --git a/Sources/FeatherOpenAPI/Path.swift b/Sources/FeatherOpenAPI/Path.swift index d96ee53..46d3cba 100644 --- a/Sources/FeatherOpenAPI/Path.swift +++ b/Sources/FeatherOpenAPI/Path.swift @@ -1,3 +1,9 @@ +// +// Path.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // @@ -5,34 +11,58 @@ // Created by Tibor Bodecs on 25/01/2024. // +/// A lightweight OpenAPI path wrapper with composition helpers. public struct Path: ExpressibleByStringLiteral { + /// The underlying path string. public let value: String + /// Creates a new path from a string. + /// - Parameter value: The path string. public init(_ value: String) { self.value = value } + /// Creates a new path from a string literal. + /// - Parameter value: The path string. public init(stringLiteral value: StringLiteralType) { self.value = value } + /// Joins two paths with a `/` separator. + /// - Parameters: + /// - lhs: The left-hand path. + /// - rhs: The right-hand path. + /// - Returns: The combined path. public static func / (lhs: Self, rhs: Self) -> Self { .init(lhs.value + "/" + rhs.value) } + /// Joins a path and a string segment. + /// - Parameters: + /// - lhs: The left-hand path. + /// - rhs: The right-hand path segment. + /// - Returns: The combined path. public static func / (lhs: Self, rhs: String) -> Self { lhs / Self(rhs) } + /// Joins a string segment and a path. + /// - Parameters: + /// - lhs: The left-hand path segment. + /// - rhs: The right-hand path. + /// - Returns: The combined path. public static func / (lhs: String, rhs: Self) -> Self { Self(lhs) / rhs } } -public extension Path { +extension Path { - static func parameter(_ param: String) -> Self { + /// Builds a path parameter segment like `{id}`. + /// - Parameter param: The parameter name. + /// - Returns: The parameterized path segment. + public static func parameter(_ param: String) -> Self { .init("{" + param + "}") } } diff --git a/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift b/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift index cfeb2a1..9d8e41e 100644 --- a/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift +++ b/Sources/FeatherOpenAPI/PathCollection/PathCollectionRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// PathCollectionRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,6 +7,7 @@ import OpenAPIKit30 +/// Describes a collection of paths and their derived components. public protocol PathCollectionRepresentable: ReferencedSchemaMapRepresentable, ReferencedParameterMapRepresentable, @@ -16,15 +17,20 @@ public protocol PathCollectionRepresentable: ReferencedTagMapRepresentable, ReferencedSecuritySchemeMapRepresentable { + /// The map of paths to path items. var pathMap: PathMap { get } + /// Derived components from the path collection. var components: FeatherOpenAPI.Components { get } } -public extension PathCollectionRepresentable { - - var referencedSchemaMap: OrderedDictionary { +extension PathCollectionRepresentable { + + /// Aggregated referenced schemas from the path map. + public var referencedSchemaMap: + OrderedDictionary + { var results = OrderedDictionary() - + let schemaMaps = pathMap.values .map { $0.referencedSchemaMap } .flatMap { $0 } @@ -34,10 +40,15 @@ public extension PathCollectionRepresentable { } return results } - - var referencedParameterMap: OrderedDictionary { - var results = OrderedDictionary() - + + /// Aggregated referenced parameters from the path map. + public var referencedParameterMap: + OrderedDictionary + { + var results = OrderedDictionary< + ParameterID, OpenAPIParameterRepresentable + >() + let parameterMaps = pathMap.values .map { $0.referencedParameterMap } .flatMap { $0 } @@ -48,8 +59,13 @@ public extension PathCollectionRepresentable { return results } - var referencedRequestBodyMap: OrderedDictionary { - var results = OrderedDictionary() + /// Aggregated referenced request bodies from the path map. + public var referencedRequestBodyMap: + OrderedDictionary + { + var results = OrderedDictionary< + RequestBodyID, OpenAPIRequestBodyRepresentable + >() let requestBodyMaps = pathMap.values .map { $0.referencedRequestBodyMap } @@ -61,7 +77,10 @@ public extension PathCollectionRepresentable { return results } - var referencedHeaderMap: OrderedDictionary { + /// Aggregated referenced headers from the path map. + public var referencedHeaderMap: + OrderedDictionary + { var results = OrderedDictionary() let headerMaps = pathMap.values @@ -74,8 +93,13 @@ public extension PathCollectionRepresentable { return results } - var referencedResponseMap: OrderedDictionary { - var results = OrderedDictionary() + /// Aggregated referenced responses from the path map. + public var referencedResponseMap: + OrderedDictionary + { + var results = OrderedDictionary< + ResponseID, OpenAPIResponseRepresentable + >() let responseMaps = pathMap.values .map { $0.referencedResponseMap } @@ -87,15 +111,20 @@ public extension PathCollectionRepresentable { return results } - var referencedTags: [OpenAPITagRepresentable] { + /// Aggregated referenced tags from the path map. + public var referencedTags: [OpenAPITagRepresentable] { pathMap.values.map { $0.referencedTags }.flatMap { $0 } } - - var referencedSecurityRequirements: [SecurityRequirementRepresentable] { + + /// Aggregated referenced security requirements from the path map. + public var referencedSecurityRequirements: + [SecurityRequirementRepresentable] + { pathMap.values.map { $0.referencedSecurityRequirements }.flatMap { $0 } } - var components: FeatherOpenAPI.Components { + /// Builds components from all referenced objects in the path map. + public var components: FeatherOpenAPI.Components { .init( schemas: referencedSchemaMap, parameters: referencedParameterMap, diff --git a/Sources/FeatherOpenAPI/PathItem/OpenAPIPathItemRepresentable.swift b/Sources/FeatherOpenAPI/PathItem/OpenAPIPathItemRepresentable.swift index 1d29cba..37b0353 100644 --- a/Sources/FeatherOpenAPI/PathItem/OpenAPIPathItemRepresentable.swift +++ b/Sources/FeatherOpenAPI/PathItem/OpenAPIPathItemRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPIPathItemRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,12 +7,17 @@ import OpenAPIKit30 +/// A type that can produce an OpenAPI path item. public protocol OpenAPIPathItemRepresentable { + /// Returns the OpenAPI path item representation. + /// - Returns: The OpenAPI path item. func openAPIPathItem() -> OpenAPI.PathItem } extension OpenAPI.PathItem: OpenAPIPathItemRepresentable { - + + /// Returns `self` as an OpenAPI path item. + /// - Returns: The current path item value. public func openAPIPathItem() -> OpenAPI.PathItem { self } diff --git a/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift b/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift index 4d11fc8..557c95f 100644 --- a/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift +++ b/Sources/FeatherOpenAPI/PathItem/PathItemRepresentable.swift @@ -1,5 +1,5 @@ // -// File 2.swift +// PathItemRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -7,6 +7,7 @@ import OpenAPIKit30 +/// Describes an OpenAPI path item with HTTP operation bindings. public protocol PathItemRepresentable: OpenAPIPathItemRepresentable, // properties @@ -21,34 +22,56 @@ public protocol PathItemRepresentable: ReferencedTagMapRepresentable, ReferencedSecuritySchemeMapRepresentable { + /// Short summary for the path item. var summary: String? { get } - + + /// Optional servers that override document-level servers. var servers: [OpenAPIServerRepresentable]? { get } + /// GET operation for this path. var get: OperationRepresentable? { get } + /// PUT operation for this path. var put: OperationRepresentable? { get } + /// POST operation for this path. var post: OperationRepresentable? { get } + /// DELETE operation for this path. var delete: OperationRepresentable? { get } + /// OPTIONS operation for this path. var options: OperationRepresentable? { get } + /// HEAD operation for this path. var head: OperationRepresentable? { get } + /// PATCH operation for this path. var patch: OperationRepresentable? { get } + /// TRACE operation for this path. var trace: OperationRepresentable? { get } } -public extension PathItemRepresentable { - - var summary: String? { nil } - - var servers: [OpenAPIServerRepresentable]? { nil } - var get: OperationRepresentable? { nil } - var put: OperationRepresentable? { nil } - var post: OperationRepresentable? { nil } - var delete: OperationRepresentable? { nil } - var options: OperationRepresentable? { nil } - var head: OperationRepresentable? { nil } - var patch: OperationRepresentable? { nil } - var trace: OperationRepresentable? { nil } - - func openAPIPathItem() -> OpenAPI.PathItem { +extension PathItemRepresentable { + + /// Default summary is `nil`. + public var summary: String? { nil } + + /// Default servers list is `nil`. + public var servers: [OpenAPIServerRepresentable]? { nil } + /// Default GET operation is `nil`. + public var get: OperationRepresentable? { nil } + /// Default PUT operation is `nil`. + public var put: OperationRepresentable? { nil } + /// Default POST operation is `nil`. + public var post: OperationRepresentable? { nil } + /// Default DELETE operation is `nil`. + public var delete: OperationRepresentable? { nil } + /// Default OPTIONS operation is `nil`. + public var options: OperationRepresentable? { nil } + /// Default HEAD operation is `nil`. + public var head: OperationRepresentable? { nil } + /// Default PATCH operation is `nil`. + public var patch: OperationRepresentable? { nil } + /// Default TRACE operation is `nil`. + public var trace: OperationRepresentable? { nil } + + /// Builds an OpenAPI path item. + /// - Returns: The OpenAPI path item. + public func openAPIPathItem() -> OpenAPI.PathItem { .init( summary: summary, description: description, @@ -65,8 +88,9 @@ public extension PathItemRepresentable { vendorExtensions: vendorExtensions ) } - - var allOperations: [OperationRepresentable] { + + /// All non-nil operations declared on the path item. + public var allOperations: [OperationRepresentable] { [ get, put, @@ -79,33 +103,48 @@ public extension PathItemRepresentable { ] .compactMap { $0 } } - - var referencedSchemaMap: OrderedDictionary { + + /// Aggregated referenced schemas from operations. + public var referencedSchemaMap: + OrderedDictionary + { var results = OrderedDictionary() - + let maps = allOperations.map { $0.referencedSchemaMap }.flatMap { $0 } - + for (k, v) in maps { results[k] = v } return results } - - var referencedParameterMap: OrderedDictionary { - var results = OrderedDictionary() - - let maps = allOperations.map { $0.referencedParameterMap }.flatMap { $0 } - + + /// Aggregated referenced parameters from operations. + public var referencedParameterMap: + OrderedDictionary + { + var results = OrderedDictionary< + ParameterID, OpenAPIParameterRepresentable + >() + + let maps = allOperations.map { $0.referencedParameterMap } + .flatMap { $0 } + for (k, v) in maps { results[k] = v } return results } - var referencedRequestBodyMap: OrderedDictionary { - var results = OrderedDictionary() + /// Aggregated referenced request bodies from operations. + public var referencedRequestBodyMap: + OrderedDictionary + { + var results = OrderedDictionary< + RequestBodyID, OpenAPIRequestBodyRepresentable + >() - let maps = allOperations.map { $0.referencedRequestBodyMap }.flatMap { $0 } + let maps = allOperations.map { $0.referencedRequestBodyMap } + .flatMap { $0 } for (k, v) in maps { results[k] = v @@ -113,7 +152,10 @@ public extension PathItemRepresentable { return results } - var referencedHeaderMap: OrderedDictionary { + /// Aggregated referenced headers from operations. + public var referencedHeaderMap: + OrderedDictionary + { var results = OrderedDictionary() let maps = allOperations.map { $0.referencedHeaderMap }.flatMap { $0 } @@ -124,8 +166,13 @@ public extension PathItemRepresentable { return results } - var referencedResponseMap: OrderedDictionary { - var results = OrderedDictionary() + /// Aggregated referenced responses from operations. + public var referencedResponseMap: + OrderedDictionary + { + var results = OrderedDictionary< + ResponseID, OpenAPIResponseRepresentable + >() let maps = allOperations.map { $0.referencedResponseMap }.flatMap { $0 } @@ -135,11 +182,15 @@ public extension PathItemRepresentable { return results } - var referencedTags: [OpenAPITagRepresentable] { + /// Aggregated referenced tags from operations. + public var referencedTags: [OpenAPITagRepresentable] { allOperations.map { $0.referencedTags }.flatMap { $0 } } - - var referencedSecurityRequirements: [SecurityRequirementRepresentable] { + + /// Aggregated referenced security requirements from operations. + public var referencedSecurityRequirements: + [SecurityRequirementRepresentable] + { allOperations.map { $0.referencedSecurityRequirements }.flatMap { $0 } } } diff --git a/Sources/FeatherOpenAPI/PathItem/PathMap.swift b/Sources/FeatherOpenAPI/PathItem/PathMap.swift index 335f4ff..0419e46 100644 --- a/Sources/FeatherOpenAPI/PathItem/PathMap.swift +++ b/Sources/FeatherOpenAPI/PathItem/PathMap.swift @@ -1,5 +1,5 @@ // -// File.swift +// PathMap.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,8 +7,8 @@ import OpenAPIKit30 -public typealias PathMap = OrderedDictionary -< +/// Ordered map of OpenAPI paths to path items. +public typealias PathMap = OrderedDictionary< OpenAPI.Path, PathItemRepresentable > diff --git a/Sources/FeatherOpenAPI/Reference/ExampleReferenceRepresentable.swift b/Sources/FeatherOpenAPI/Reference/ExampleReferenceRepresentable.swift index 5ec3b88..e1eac36 100644 --- a/Sources/FeatherOpenAPI/Reference/ExampleReferenceRepresentable.swift +++ b/Sources/FeatherOpenAPI/Reference/ExampleReferenceRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// ExampleReferenceRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 24.. @@ -7,25 +7,38 @@ import OpenAPIKit30 +/// A type that exposes a referenced example. public protocol ExampleReferenceRepresentable { + /// The identifier for the example reference. var id: ExampleID { get } + /// The underlying example object. var object: ExampleRepresentable { get } } +/// Wrapper that exposes an example as a reusable reference. public struct ExampleReference: ExampleRepresentable, ExampleReferenceRepresentable { + /// Example summary. public var summary: String? { object.summary } + /// Example value. public var value: AnyCodable { object.value } + /// Example description. public var description: String? { object.description } - public var vendorExtensions: [String: AnyCodable] { object.vendorExtensions } + /// Example vendor extensions. + public var vendorExtensions: [String: AnyCodable] { + object.vendorExtensions + } + /// The underlying example object. public var object: ExampleRepresentable { _object } + /// The example identifier. public var id: ExampleID + /// The concrete example instance. public var _object: T internal init( @@ -35,7 +48,11 @@ public struct ExampleReference: self._object = object } - public func openAPIExample() -> Either, OpenAPI.Example> { + /// Returns a component reference for the example. + /// - Returns: An example reference. + public func openAPIExample() -> Either< + JSONReference, OpenAPI.Example + > { .reference(.component(named: id.rawValue)) } } diff --git a/Sources/FeatherOpenAPI/Reference/HeaderReferenceRepresentable.swift b/Sources/FeatherOpenAPI/Reference/HeaderReferenceRepresentable.swift index 7feaa3c..c531228 100644 --- a/Sources/FeatherOpenAPI/Reference/HeaderReferenceRepresentable.swift +++ b/Sources/FeatherOpenAPI/Reference/HeaderReferenceRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// HeaderReferenceRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,24 +7,34 @@ import OpenAPIKit30 +/// A type that exposes a referenced header. public protocol HeaderReferenceRepresentable { + /// The identifier for the header reference. var id: HeaderID { get } + /// The underlying header object. var object: HeaderRepresentable { get } } +/// Wrapper that exposes a header as a reusable reference. public struct HeaderReference: HeaderRepresentable, HeaderReferenceRepresentable { + /// Header schema. public var schema: OpenAPISchemaRepresentable { object.schema } + /// The underlying header object. public var object: HeaderRepresentable { _object } + /// The header identifier. public var id: HeaderID + /// The concrete header instance. public var _object: T + /// Creates a header reference. + /// - Parameter object: The header to reference. public init( _ object: T ) { @@ -32,7 +42,11 @@ public struct HeaderReference: self._object = object } - public func openAPIHeader() -> Either, OpenAPI.Header> { + /// Returns a component reference for the header. + /// - Returns: A header reference. + public func openAPIHeader() -> Either< + JSONReference, OpenAPI.Header + > { .reference(.component(named: id.rawValue)) } } diff --git a/Sources/FeatherOpenAPI/Reference/ParameterReferenceRepresentable.swift b/Sources/FeatherOpenAPI/Reference/ParameterReferenceRepresentable.swift index 30f14c1..2c8feab 100644 --- a/Sources/FeatherOpenAPI/Reference/ParameterReferenceRepresentable.swift +++ b/Sources/FeatherOpenAPI/Reference/ParameterReferenceRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// ParameterReferenceRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,26 +7,38 @@ import OpenAPIKit30 +/// A type that exposes a referenced parameter. public protocol ParameterReferenceRepresentable { + /// The identifier for the parameter reference. var id: ParameterID { get } + /// The underlying parameter object. var object: ParameterRepresentable { get } } +/// Wrapper that exposes a parameter as a reusable reference. public struct ParameterReference: ParameterRepresentable, ParameterReferenceRepresentable { + /// Parameter name. public var name: String { object.name } - public var context: OpenAPIKit30.OpenAPI.Parameter.Context { object.context } + /// Parameter context. + public var context: OpenAPIKit30.OpenAPI.Parameter.Context { + object.context + } + /// Parameter schema. public var schema: any OpenAPISchemaRepresentable { object.schema } - + + /// The underlying parameter object. public var object: ParameterRepresentable { _object } + /// The parameter identifier. public var id: ParameterID + /// The concrete parameter instance. public var _object: T - + internal init( _ object: T ) { @@ -34,7 +46,11 @@ public struct ParameterReference: self._object = object } - public func openAPIParameter() -> Either, OpenAPI.Parameter> { + /// Returns a component reference for the parameter. + /// - Returns: A parameter reference. + public func openAPIParameter() -> Either< + JSONReference, OpenAPI.Parameter + > { .reference(.component(named: id.rawValue)) } } diff --git a/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift b/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift index 69fa53e..b79c122 100644 --- a/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift +++ b/Sources/FeatherOpenAPI/Reference/ReferencedSchemaMapRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// ReferencedSchemaMapRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,30 +7,56 @@ import OpenAPIKit30 +/// Exposes referenced schemas. public protocol ReferencedSchemaMapRepresentable { - var referencedSchemaMap: OrderedDictionary { get } + /// Map of referenced schemas. + var referencedSchemaMap: + OrderedDictionary + { get } } +/// Exposes referenced parameters. public protocol ReferencedParameterMapRepresentable { - var referencedParameterMap: OrderedDictionary { get } + /// Map of referenced parameters. + var referencedParameterMap: + OrderedDictionary + { get } } +/// Exposes referenced request bodies. public protocol ReferencedRequestBodyMapRepresentable { - var referencedRequestBodyMap: OrderedDictionary { get } + /// Map of referenced request bodies. + var referencedRequestBodyMap: + OrderedDictionary + { get } } +/// Exposes referenced headers. public protocol ReferencedHeaderMapRepresentable { - var referencedHeaderMap: OrderedDictionary { get } + /// Map of referenced headers. + var referencedHeaderMap: + OrderedDictionary + { get } } +/// Exposes referenced responses. public protocol ReferencedResponseMapRepresentable { - var referencedResponseMap: OrderedDictionary { get } + /// Map of referenced responses. + var referencedResponseMap: + OrderedDictionary + { get } } +/// Exposes referenced security requirements. public protocol ReferencedSecuritySchemeMapRepresentable { - var referencedSecurityRequirements: [SecurityRequirementRepresentable] { get } + /// List of referenced security requirements. + var referencedSecurityRequirements: [SecurityRequirementRepresentable] { + get + } } +/// Exposes referenced tags. public protocol ReferencedTagMapRepresentable { + /// List of referenced tags. var referencedTags: [OpenAPITagRepresentable] { get } } diff --git a/Sources/FeatherOpenAPI/Reference/RequestBodyReferenceRepresentable.swift b/Sources/FeatherOpenAPI/Reference/RequestBodyReferenceRepresentable.swift index 5fa1991..bcb44df 100644 --- a/Sources/FeatherOpenAPI/Reference/RequestBodyReferenceRepresentable.swift +++ b/Sources/FeatherOpenAPI/Reference/RequestBodyReferenceRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// RequestBodyReferenceRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,22 +7,30 @@ import OpenAPIKit30 +/// A type that exposes a referenced request body. public protocol RequestBodyReferenceRepresentable { + /// The identifier for the request body reference. var id: RequestBodyID { get } + /// The underlying request body object. var object: RequestBodyRepresentable { get } } +/// Wrapper that exposes a request body as a reusable reference. public struct RequestBodyReference: RequestBodyRepresentable, RequestBodyReferenceRepresentable { + /// Request body content map. public var contentMap: ContentMap { object.contentMap } + /// The underlying request body object. public var object: RequestBodyRepresentable { _object } + /// The request body identifier. public var id: RequestBodyID + /// The concrete request body instance. public var _object: T internal init( @@ -32,7 +40,11 @@ public struct RequestBodyReference: self._object = object } - public func openAPIRequestBody() -> Either, OpenAPI.Request> { + /// Returns a component reference for the request body. + /// - Returns: A request body reference. + public func openAPIRequestBody() -> Either< + JSONReference, OpenAPI.Request + > { .reference(.component(named: id.rawValue)) } } diff --git a/Sources/FeatherOpenAPI/Reference/ResponseReferenceRepresentable.swift b/Sources/FeatherOpenAPI/Reference/ResponseReferenceRepresentable.swift index a6c362c..5f0a5e2 100644 --- a/Sources/FeatherOpenAPI/Reference/ResponseReferenceRepresentable.swift +++ b/Sources/FeatherOpenAPI/Reference/ResponseReferenceRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// ResponseReferenceRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,24 +7,34 @@ import OpenAPIKit30 +/// A type that exposes a referenced response. public protocol ResponseReferenceRepresentable { + /// The identifier for the response reference. var id: ResponseID { get } + /// The underlying response object. var object: ResponseRepresentable { get } } +/// Wrapper that exposes a response as a reusable reference. public struct ResponseReference: ResponseRepresentable, ResponseReferenceRepresentable { + /// Response description. public var description: String { object.description } + /// Response header map. public var headerMap: HeaderMap { object.headerMap } + /// Response content map. public var contentMap: ContentMap { object.contentMap } + /// The underlying response object. public var object: ResponseRepresentable { _object } + /// The response identifier. public var id: ResponseID + /// The concrete response instance. public var _object: T internal init( @@ -34,7 +44,11 @@ public struct ResponseReference: self._object = object } - public func openAPIResponse() -> Either, OpenAPI.Response> { + /// Returns a component reference for the response. + /// - Returns: A response reference. + public func openAPIResponse() -> Either< + JSONReference, OpenAPI.Response + > { .reference(.component(named: id.rawValue)) } } diff --git a/Sources/FeatherOpenAPI/Reference/SchemaReference.swift b/Sources/FeatherOpenAPI/Reference/SchemaReference.swift index ff0db62..0a220b1 100644 --- a/Sources/FeatherOpenAPI/Reference/SchemaReference.swift +++ b/Sources/FeatherOpenAPI/Reference/SchemaReference.swift @@ -1,5 +1,5 @@ // -// File.swift +// SchemaReference.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -7,23 +7,31 @@ import OpenAPIKit30 +/// A type that exposes a referenced schema. public protocol SchemaReferenceRepresentable { + /// The identifier for the schema reference. var id: SchemaID { get } + /// The underlying schema object. var object: SchemaRepresentable { get } } +/// Wrapper that exposes a schema as a reusable reference. public struct SchemaReference: SchemaRepresentable, SchemaReferenceRepresentable { + /// The underlying schema object. public var object: any SchemaRepresentable { _object } + /// The schema identifier. public var id: SchemaID + /// The concrete schema instance. public var _object: T + /// Indicates whether the reference is required. public var required: Bool - + internal init( _ object: T, required: Bool = true @@ -33,11 +41,16 @@ public struct SchemaReference: self.required = required } + /// Returns a JSON Schema reference to the component. + /// - Returns: The JSON schema reference. public func openAPISchema() -> JSONSchema { .reference(.component(named: id.rawValue), required: required) } - public var referencedSchemaMap: OrderedDictionary { + /// Referenced schema map for this reference. + public var referencedSchemaMap: + OrderedDictionary + { [id: object] } } diff --git a/Sources/FeatherOpenAPI/RequestBody/Abstraction/OpenAPIRequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/Abstraction/OpenAPIRequestBodyRepresentable.swift index 0192ad2..96d6f3c 100644 --- a/Sources/FeatherOpenAPI/RequestBody/Abstraction/OpenAPIRequestBodyRepresentable.swift +++ b/Sources/FeatherOpenAPI/RequestBody/Abstraction/OpenAPIRequestBodyRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPIRequestBodyRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -7,13 +7,22 @@ import OpenAPIKit30 +/// A type that can produce an OpenAPI request body or reference. public protocol OpenAPIRequestBodyRepresentable { - func openAPIRequestBody() -> Either, OpenAPI.Request> + /// Returns the OpenAPI request body representation. + /// - Returns: An OpenAPI request body or reference. + func openAPIRequestBody() -> Either< + JSONReference, OpenAPI.Request + > } extension OpenAPI.Request: OpenAPIRequestBodyRepresentable { - - public func openAPIRequestBody() -> Either, OpenAPI.Request> { + + /// Returns `self` wrapped as an OpenAPI request body. + /// - Returns: An OpenAPI request body value. + public func openAPIRequestBody() -> Either< + JSONReference, OpenAPI.Request + > { .init(self) } } diff --git a/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyID.swift b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyID.swift index 7daa82d..b47ba93 100644 --- a/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyID.swift +++ b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyID.swift @@ -1,14 +1,18 @@ // -// File.swift +// RequestBodyID.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. // +/// Strongly typed identifier for reusable OpenAPI request bodies. public struct RequestBodyID: Sendable, Equatable, Hashable, Codable { + /// The raw identifier value. public var rawValue: String + /// Creates a request body identifier. + /// - Parameter rawValue: The raw identifier value. public init( _ rawValue: String ) { diff --git a/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyRepresentable.swift index 4e2dbf3..a96f5a0 100644 --- a/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyRepresentable.swift +++ b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// RequestBodyRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -7,6 +7,7 @@ import OpenAPIKit30 +/// Describes an OpenAPI request body with defaults. public protocol RequestBodyRepresentable: Identifiable, OpenAPIRequestBodyRepresentable, @@ -16,16 +17,23 @@ public protocol RequestBodyRepresentable: // reference ReferencedSchemaMapRepresentable { + /// Map of request body content by content type. var contentMap: ContentMap { get } } -public extension RequestBodyRepresentable { - - func reference() -> RequestBodyReference { +extension RequestBodyRepresentable { + + /// Creates a reference wrapper for this request body. + /// - Returns: A request body reference. + public func reference() -> RequestBodyReference { .init(self) } - func openAPIRequestBody() -> Either, OpenAPI.Request> { + /// Builds an OpenAPI request body object or reference. + /// - Returns: The OpenAPI request body representation. + public func openAPIRequestBody() -> Either< + JSONReference, OpenAPI.Request + > { .init( .init( description: description, @@ -35,8 +43,11 @@ public extension RequestBodyRepresentable { ) ) } - - var referencedSchemaMap: OrderedDictionary { + + /// Aggregated referenced schemas from the content map. + public var referencedSchemaMap: + OrderedDictionary + { var results = OrderedDictionary() for content in contentMap.values { results.merge(content.referencedSchemaMap) diff --git a/Sources/FeatherOpenAPI/RequestBody/BinaryRequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/BinaryRequestBodyRepresentable.swift index 30444d7..762750a 100644 --- a/Sources/FeatherOpenAPI/RequestBody/BinaryRequestBodyRepresentable.swift +++ b/Sources/FeatherOpenAPI/RequestBody/BinaryRequestBodyRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// BinaryRequestBodyRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -7,13 +7,15 @@ import OpenAPIKit30 +/// Request body with binary content. public protocol BinaryRequestBodyRepresentable: RequestBodyRepresentable { } -public extension BinaryRequestBodyRepresentable { +extension BinaryRequestBodyRepresentable { - var contentMap: ContentMap { + /// Builds a binary content map using an octet-stream schema. + public var contentMap: ContentMap { [ .other("application/octet-stream"): Content(BinarySchema()) ] diff --git a/Sources/FeatherOpenAPI/RequestBody/FormRequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/FormRequestBodyRepresentable.swift index b2d0e5d..769c183 100644 --- a/Sources/FeatherOpenAPI/RequestBody/FormRequestBodyRepresentable.swift +++ b/Sources/FeatherOpenAPI/RequestBody/FormRequestBodyRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// FormRequestBodyRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -7,18 +7,21 @@ import OpenAPIKit30 +/// Request body with form-encoded content. public protocol FormRequestBodyRepresentable: RequestBodyRepresentable { + /// The schema type used in the form content. associatedtype SchemaType: SchemaRepresentable - + + /// The schema instance for the form request body. var schema: SchemaType { get } } -public extension FormRequestBodyRepresentable { +extension FormRequestBodyRepresentable { - var contentMap: ContentMap { + /// Builds a form content map from the schema. + public var contentMap: ContentMap { [ .form: Content(schema) ] } } - diff --git a/Sources/FeatherOpenAPI/RequestBody/JSONRequestBodyRepresentable.swift b/Sources/FeatherOpenAPI/RequestBody/JSONRequestBodyRepresentable.swift index 5c4c91d..2e7cd16 100644 --- a/Sources/FeatherOpenAPI/RequestBody/JSONRequestBodyRepresentable.swift +++ b/Sources/FeatherOpenAPI/RequestBody/JSONRequestBodyRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// JSONRequestBodyRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -7,18 +7,21 @@ import OpenAPIKit30 +/// Request body with JSON content. public protocol JSONRequestBodyRepresentable: RequestBodyRepresentable { + /// The JSON schema type used in the request body. associatedtype SchemaType: SchemaRepresentable - + + /// The schema instance for the JSON request body. var schema: SchemaType { get } } -public extension JSONRequestBodyRepresentable { +extension JSONRequestBodyRepresentable { - var contentMap: ContentMap { + /// Builds a JSON content map from the schema. + public var contentMap: ContentMap { [ .json: Content(schema) ] } } - diff --git a/Sources/FeatherOpenAPI/Response/Abstraction/OpenAPIResponseRepresentable.swift b/Sources/FeatherOpenAPI/Response/Abstraction/OpenAPIResponseRepresentable.swift index 824a9a2..38204c6 100644 --- a/Sources/FeatherOpenAPI/Response/Abstraction/OpenAPIResponseRepresentable.swift +++ b/Sources/FeatherOpenAPI/Response/Abstraction/OpenAPIResponseRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPIResponseRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -7,13 +7,22 @@ import OpenAPIKit30 +/// A type that can produce an OpenAPI response or reference. public protocol OpenAPIResponseRepresentable { - func openAPIResponse() -> Either, OpenAPI.Response> + /// Returns the OpenAPI response representation. + /// - Returns: An OpenAPI response or reference. + func openAPIResponse() -> Either< + JSONReference, OpenAPI.Response + > } extension OpenAPI.Response: OpenAPIResponseRepresentable { - public func openAPIResponse() -> Either, OpenAPI.Response> { + /// Returns `self` wrapped as an OpenAPI response. + /// - Returns: An OpenAPI response value. + public func openAPIResponse() -> Either< + JSONReference, OpenAPI.Response + > { .init(self) } } diff --git a/Sources/FeatherOpenAPI/Response/Abstraction/ResponseID.swift b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseID.swift index 05cfd08..8c8a663 100644 --- a/Sources/FeatherOpenAPI/Response/Abstraction/ResponseID.swift +++ b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseID.swift @@ -1,14 +1,18 @@ // -// File.swift +// ResponseID.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. // +/// Strongly typed identifier for reusable OpenAPI responses. public struct ResponseID: Sendable, Equatable, Hashable, Codable { + /// The raw identifier value. public var rawValue: String + /// Creates a response identifier. + /// - Parameter rawValue: The raw identifier value. public init( _ rawValue: String ) { diff --git a/Sources/FeatherOpenAPI/Response/Abstraction/ResponseMap.swift b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseMap.swift index f78b664..7962a6a 100644 --- a/Sources/FeatherOpenAPI/Response/Abstraction/ResponseMap.swift +++ b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseMap.swift @@ -1,5 +1,5 @@ // -// File.swift +// ResponseMap.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,8 +7,8 @@ import OpenAPIKit30 -public typealias ResponseMap = OrderedDictionary -< +/// Ordered map of response status codes to response definitions. +public typealias ResponseMap = OrderedDictionary< OpenAPI.Response.StatusCode, ResponseRepresentable > diff --git a/Sources/FeatherOpenAPI/Response/Abstraction/ResponseRepresentable.swift b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseRepresentable.swift index d89c094..ec5c156 100644 --- a/Sources/FeatherOpenAPI/Response/Abstraction/ResponseRepresentable.swift +++ b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// ResponseRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -7,25 +7,36 @@ import OpenAPIKit30 +/// Describes an OpenAPI response with defaults. public protocol ResponseRepresentable: OpenAPIResponseRepresentable, Identifiable, VendorExtensionsProperty { + /// Response description. var description: String { get } + /// Map of response headers. var headerMap: HeaderMap { get } + /// Map of response content by content type. var contentMap: ContentMap { get } } -public extension ResponseRepresentable { +extension ResponseRepresentable { - var headerMap: HeaderMap { [:] } - - func reference() -> ResponseReference { + /// Default header map is empty. + public var headerMap: HeaderMap { [:] } + + /// Creates a reference wrapper for this response. + /// - Returns: A response reference. + public func reference() -> ResponseReference { .init(self) } - func openAPIResponse() -> Either, OpenAPI.Response> { + /// Builds an OpenAPI response object or reference. + /// - Returns: The OpenAPI response representation. + public func openAPIResponse() -> Either< + JSONReference, OpenAPI.Response + > { .init( .init( description: description, diff --git a/Sources/FeatherOpenAPI/Response/BinaryResponseRepresentable.swift b/Sources/FeatherOpenAPI/Response/BinaryResponseRepresentable.swift index 2e66ea0..6f8743e 100644 --- a/Sources/FeatherOpenAPI/Response/BinaryResponseRepresentable.swift +++ b/Sources/FeatherOpenAPI/Response/BinaryResponseRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// BinaryResponseRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,16 +7,17 @@ import OpenAPIKit30 +/// Response with binary content. public protocol BinaryResponseRepresentable: ResponseRepresentable { } -public extension BinaryResponseRepresentable { +extension BinaryResponseRepresentable { - var contentMap: ContentMap { + /// Builds a binary content map using an octet-stream schema. + public var contentMap: ContentMap { [ .other("application/octet-stream"): Content(BinarySchema()) ] } } - diff --git a/Sources/FeatherOpenAPI/Response/FormResponseRepresentable.swift b/Sources/FeatherOpenAPI/Response/FormResponseRepresentable.swift index 66d5399..ca00ede 100644 --- a/Sources/FeatherOpenAPI/Response/FormResponseRepresentable.swift +++ b/Sources/FeatherOpenAPI/Response/FormResponseRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// FormResponseRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,18 +7,21 @@ import OpenAPIKit30 +/// Response with form-encoded content. public protocol FormResponseRepresentable: ResponseRepresentable { + /// The schema type used in the form content. associatedtype SchemaType: SchemaRepresentable - + + /// The schema instance for the form response. var schema: SchemaType { get } } -public extension FormResponseRepresentable { +extension FormResponseRepresentable { - var contentMap: ContentMap { + /// Builds a form content map from the schema. + public var contentMap: ContentMap { [ .form: Content(schema) ] } } - diff --git a/Sources/FeatherOpenAPI/Response/JSONResponseRepresentable.swift b/Sources/FeatherOpenAPI/Response/JSONResponseRepresentable.swift index 6303779..b338180 100644 --- a/Sources/FeatherOpenAPI/Response/JSONResponseRepresentable.swift +++ b/Sources/FeatherOpenAPI/Response/JSONResponseRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// JSONResponseRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,18 +7,21 @@ import OpenAPIKit30 +/// Response with JSON content. public protocol JSONResponseRepresentable: ResponseRepresentable { + /// The JSON schema type used in the response content. associatedtype SchemaType: SchemaRepresentable - + + /// The schema instance for the JSON response. var schema: SchemaType { get } } -public extension JSONResponseRepresentable { +extension JSONResponseRepresentable { - var contentMap: ContentMap { + /// Builds a JSON content map from the schema. + public var contentMap: ContentMap { [ .json: Content(schema) ] } } - diff --git a/Sources/FeatherOpenAPI/Schema/Abstraction/OpenAPISchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/OpenAPISchemaRepresentable.swift index 83e6a3d..a0888a2 100644 --- a/Sources/FeatherOpenAPI/Schema/Abstraction/OpenAPISchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/Abstraction/OpenAPISchemaRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPISchemaRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -7,14 +7,17 @@ import OpenAPIKit30 +/// A type that can produce an OpenAPI JSON schema. public protocol OpenAPISchemaRepresentable { + /// Returns the JSON schema representation. + /// - Returns: The JSON schema. func openAPISchema() -> JSONSchema } extension JSONSchema: OpenAPISchemaRepresentable { + /// Returns `self` as a JSON schema. + /// - Returns: The current schema value. public func openAPISchema() -> JSONSchema { self } } - - diff --git a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaID.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaID.swift index 0349d58..f269041 100644 --- a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaID.swift +++ b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaID.swift @@ -1,14 +1,18 @@ // -// File.swift +// SchemaID.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. // +/// Strongly typed identifier for reusable OpenAPI schemas. public struct SchemaID: Sendable, Equatable, Hashable, Codable { + /// The raw identifier value. public var rawValue: String + /// Creates a schema identifier. + /// - Parameter rawValue: The raw identifier value. public init( _ rawValue: String ) { diff --git a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaMap.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaMap.swift index bb2185b..6151b52 100644 --- a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaMap.swift +++ b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaMap.swift @@ -1,5 +1,5 @@ // -// File.swift +// SchemaMap.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,8 +7,8 @@ import OpenAPIKit30 -public typealias SchemaMap = OrderedDictionary -< +/// Ordered map of schema names to schema definitions. +public typealias SchemaMap = OrderedDictionary< String, SchemaRepresentable > diff --git a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift index 18f1f98..e93e589 100644 --- a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// SchemaRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,6 +7,7 @@ import OpenAPIKit30 +/// Describes an OpenAPI schema with common properties. public protocol SchemaRepresentable: OpenAPISchemaRepresentable, Identifiable, @@ -17,24 +18,36 @@ public protocol SchemaRepresentable: DescriptionProperty, NullableProperty { + /// Indicates whether the schema is deprecated. var deprecated: Bool? { get } } -public extension SchemaRepresentable { - - func reference( +extension SchemaRepresentable { + + /// Creates a reference wrapper for this schema. + /// - Parameter required: Whether the reference is required. + /// - Returns: A schema reference. + public func reference( required: Bool = true ) -> SchemaReference { .init(self) } - - var deprecated: Bool? { nil } - var referencedSchemaMap: OrderedDictionary { - return [:] + /// Default deprecated flag is `nil`. + public var deprecated: Bool? { nil } + + /// Referenced schemas directly used by this schema. + public var referencedSchemaMap: + OrderedDictionary + { + [:] } - func allReferencedSchemaMap() -> OrderedDictionary { + /// Collects all referenced schemas transitively. + /// - Returns: An ordered dictionary of all referenced schemas. + public func allReferencedSchemaMap() -> OrderedDictionary< + SchemaID, OpenAPISchemaRepresentable + > { var results = OrderedDictionary() var visited = Set() collectReferencedSchemaMap(into: &results, visited: &visited) @@ -42,16 +55,18 @@ public extension SchemaRepresentable { } fileprivate func collectReferencedSchemaMap( - into results: inout OrderedDictionary, + into results: + inout OrderedDictionary, visited: inout Set ) { - for (id, schema) in referencedSchemaMap { - guard visited.insert(id).inserted else { - continue - } + for (id, schema) in referencedSchemaMap + where visited.insert(id).inserted { results[id] = schema if let schema = schema as? SchemaRepresentable { - schema.collectReferencedSchemaMap(into: &results, visited: &visited) + schema.collectReferencedSchemaMap( + into: &results, + visited: &visited + ) } } } diff --git a/Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift index f062ab0..f257efe 100644 --- a/Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/ArraySchemaRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// ArraySchemaRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -7,15 +7,19 @@ import OpenAPIKit30 +/// Schema representation for arrays. public protocol ArraySchemaRepresentable: SchemaRepresentable { + /// The item schema for the array. var items: SchemaRepresentable? { get } } -public extension ArraySchemaRepresentable { +extension ArraySchemaRepresentable { - func openAPISchema() -> JSONSchema { + /// Builds an array JSON schema. + /// - Returns: The JSON schema. + public func openAPISchema() -> JSONSchema { .array( format: .generic, required: `required`, @@ -35,16 +39,19 @@ public extension ArraySchemaRepresentable { example: nil ) } - - var referencedSchemaMap: OrderedDictionary { - var results: OrderedDictionary = [:] + + /// Referenced schemas used by the array items. + public var referencedSchemaMap: + OrderedDictionary + { + var results: OrderedDictionary = + [:] for (key, value) in items?.referencedSchemaMap ?? [:] { -// if let ref = value as? SchemaReferenceRepresentable { - results[key] = value -// } + // if let ref = value as? SchemaReferenceRepresentable { + results[key] = value + // } } return results } } - diff --git a/Sources/FeatherOpenAPI/Schema/BinarySchema.swift b/Sources/FeatherOpenAPI/Schema/BinarySchema.swift index a1cec74..2f86748 100644 --- a/Sources/FeatherOpenAPI/Schema/BinarySchema.swift +++ b/Sources/FeatherOpenAPI/Schema/BinarySchema.swift @@ -1,5 +1,5 @@ // -// File.swift +// BinarySchema.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 24.. @@ -8,14 +8,16 @@ import OpenAPIKit30 struct BinarySchema: SchemaRepresentable { - + func openAPISchema() -> JSONSchema { JSONSchema.string( format: .binary ) } - var referencedSchemaMap: OrderedDictionary { + var referencedSchemaMap: + OrderedDictionary + { [:] } } diff --git a/Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift index b861d6d..d4976da 100644 --- a/Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// BoolSchemaRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -7,6 +7,7 @@ import OpenAPIKit30 +/// Schema representation for boolean values. public protocol BoolSchemaRepresentable: SchemaRepresentable, ExampleProperty, @@ -15,12 +16,14 @@ where ExamplePropertyType == Bool, DefaultValuePropertyType == Bool { - + } -public extension BoolSchemaRepresentable { +extension BoolSchemaRepresentable { - func openAPISchema() -> JSONSchema { + /// Builds a boolean JSON schema. + /// - Returns: The JSON schema. + public func openAPISchema() -> JSONSchema { .boolean( format: .generic, required: `required`, @@ -37,4 +40,3 @@ public extension BoolSchemaRepresentable { ) } } - diff --git a/Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift index 87823c4..b4a6287 100644 --- a/Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// DoubleSchemaRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -7,6 +7,7 @@ import OpenAPIKit30 +/// Schema representation for double values. public protocol DoubleSchemaRepresentable: SchemaRepresentable, ExampleProperty, @@ -17,12 +18,14 @@ where DefaultValuePropertyType == Double, AllowedValuesPropertyType == Double { - + } -public extension DoubleSchemaRepresentable { +extension DoubleSchemaRepresentable { - func openAPISchema() -> JSONSchema { + /// Builds a double JSON schema. + /// - Returns: The JSON schema. + public func openAPISchema() -> JSONSchema { .number( format: .double, required: `required`, diff --git a/Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift index 99463cf..7b641e7 100644 --- a/Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// FloatSchemaRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -7,6 +7,7 @@ import OpenAPIKit30 +/// Schema representation for float values. public protocol FloatSchemaRepresentable: SchemaRepresentable, ExampleProperty, @@ -17,12 +18,14 @@ where DefaultValuePropertyType == Float, AllowedValuesPropertyType == Float { - + } -public extension FloatSchemaRepresentable { +extension FloatSchemaRepresentable { - func openAPISchema() -> JSONSchema { + /// Builds a float JSON schema. + /// - Returns: The JSON schema. + public func openAPISchema() -> JSONSchema { .number( format: .float, required: `required`, @@ -42,4 +45,3 @@ public extension FloatSchemaRepresentable { ) } } - diff --git a/Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift index 67b710c..ce2d557 100644 --- a/Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// Int32SchemaRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -7,6 +7,7 @@ import OpenAPIKit30 +/// Schema representation for 32-bit integer values. public protocol Int32SchemaRepresentable: SchemaRepresentable, ExampleProperty, @@ -17,12 +18,14 @@ where DefaultValuePropertyType == Int32, AllowedValuesPropertyType == Int32 { - + } -public extension Int32SchemaRepresentable { +extension Int32SchemaRepresentable { - func openAPISchema() -> JSONSchema { + /// Builds an int32 JSON schema. + /// - Returns: The JSON schema. + public func openAPISchema() -> JSONSchema { .integer( format: .int32, required: `required`, diff --git a/Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift index a3292bf..43e15a4 100644 --- a/Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// Int64SchemaRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -7,6 +7,7 @@ import OpenAPIKit30 +/// Schema representation for 64-bit integer values. public protocol Int64SchemaRepresentable: SchemaRepresentable, ExampleProperty, @@ -17,12 +18,14 @@ where DefaultValuePropertyType == Int64, AllowedValuesPropertyType == Int64 { - + } -public extension Int64SchemaRepresentable { +extension Int64SchemaRepresentable { - func openAPISchema() -> JSONSchema { + /// Builds an int64 JSON schema. + /// - Returns: The JSON schema. + public func openAPISchema() -> JSONSchema { .integer( format: .int64, required: `required`, diff --git a/Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift index 164079f..3030ef6 100644 --- a/Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// IntSchemaRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -7,6 +7,7 @@ import OpenAPIKit30 +/// Schema representation for integer values. public protocol IntSchemaRepresentable: SchemaRepresentable, ExampleProperty, @@ -17,12 +18,14 @@ where DefaultValuePropertyType == Int, AllowedValuesPropertyType == Int { - + } -public extension IntSchemaRepresentable { +extension IntSchemaRepresentable { - func openAPISchema() -> JSONSchema { + /// Builds an integer JSON schema. + /// - Returns: The JSON schema. + public func openAPISchema() -> JSONSchema { .integer( format: .unspecified, required: `required`, diff --git a/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift index 05f2e97..e159534 100644 --- a/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/ObjectSchemaRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// ObjectSchemaRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -7,18 +7,22 @@ import OpenAPIKit30 +/// Schema representation for objects. public protocol ObjectSchemaRepresentable: SchemaRepresentable, ExampleProperty where ExamplePropertyType == AnyCodable { + /// Map of property names to schemas. var propertyMap: SchemaMap { get } } -public extension ObjectSchemaRepresentable { +extension ObjectSchemaRepresentable { - func openAPISchema() -> JSONSchema { + /// Builds an object JSON schema. + /// - Returns: The JSON schema. + public func openAPISchema() -> JSONSchema { .object( format: .generic, required: `required`, @@ -38,8 +42,11 @@ public extension ObjectSchemaRepresentable { example: example ) } - - var referencedSchemaMap: OrderedDictionary { + + /// Referenced schemas used by object properties. + public var referencedSchemaMap: + OrderedDictionary + { var results = OrderedDictionary() for (_, value) in propertyMap { results.merge(value.referencedSchemaMap) diff --git a/Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift index a9a49dc..f0d9223 100644 --- a/Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// StringSchemaRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -7,6 +7,7 @@ import OpenAPIKit30 +/// Schema representation for string values. public protocol StringSchemaRepresentable: SchemaRepresentable, ExampleProperty, @@ -17,12 +18,14 @@ where DefaultValuePropertyType == String, AllowedValuesPropertyType == String { - + } -public extension StringSchemaRepresentable { +extension StringSchemaRepresentable { - func openAPISchema() -> JSONSchema { + /// Builds a string JSON schema. + /// - Returns: The JSON schema. + public func openAPISchema() -> JSONSchema { .string( format: .generic, required: `required`, @@ -42,4 +45,3 @@ public extension StringSchemaRepresentable { ) } } - diff --git a/Sources/FeatherOpenAPI/SecurityRequirement/OpenAPISecurityRequirementRepresentable.swift b/Sources/FeatherOpenAPI/SecurityRequirement/OpenAPISecurityRequirementRepresentable.swift index 1945836..1e0ff1c 100644 --- a/Sources/FeatherOpenAPI/SecurityRequirement/OpenAPISecurityRequirementRepresentable.swift +++ b/Sources/FeatherOpenAPI/SecurityRequirement/OpenAPISecurityRequirementRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPISecurityRequirementRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,14 +7,19 @@ import OpenAPIKit30 +/// A type that can produce an OpenAPI security requirement. public protocol OpenAPISecurityRequirementRepresentable { // [JSONReference: [String]] + /// Returns the OpenAPI security requirement representation. + /// - Returns: The OpenAPI security requirement. func openAPISecurityRequirement() -> OpenAPI.SecurityRequirement } extension OpenAPI.SecurityRequirement: OpenAPISecurityRequirementRepresentable { + /// Returns `self` as an OpenAPI security requirement. + /// - Returns: The current security requirement value. public func openAPISecurityRequirement() -> OpenAPI.SecurityRequirement { self } diff --git a/Sources/FeatherOpenAPI/SecurityRequirement/SecurityRequirementRepresentable.swift b/Sources/FeatherOpenAPI/SecurityRequirement/SecurityRequirementRepresentable.swift index 31379ca..962f7d9 100644 --- a/Sources/FeatherOpenAPI/SecurityRequirement/SecurityRequirementRepresentable.swift +++ b/Sources/FeatherOpenAPI/SecurityRequirement/SecurityRequirementRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// SecurityRequirementRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -7,19 +7,25 @@ import OpenAPIKit30 +/// Describes a security requirement for an operation or document. public protocol SecurityRequirementRepresentable: OpenAPISecurityRequirementRepresentable { + /// The referenced security scheme. var security: any SecuritySchemeRepresentable { get } + /// The required scopes or requirements. var requirements: [String] { get } } -public extension SecurityRequirementRepresentable { +extension SecurityRequirementRepresentable { - var requirements: [String] { [] } + /// Default requirements are empty. + public var requirements: [String] { [] } //[JSONReference: [String]] - func openAPISecurityRequirement() -> OpenAPI.SecurityRequirement { + /// Builds an OpenAPI security requirement. + /// - Returns: The OpenAPI security requirement. + public func openAPISecurityRequirement() -> OpenAPI.SecurityRequirement { [ .component(named: security.openAPIIdentifier): requirements ] diff --git a/Sources/FeatherOpenAPI/SecurityScheme/OpenAPISecuritySchemeRepresentable.swift b/Sources/FeatherOpenAPI/SecurityScheme/OpenAPISecuritySchemeRepresentable.swift index d6d720f..ce29e15 100644 --- a/Sources/FeatherOpenAPI/SecurityScheme/OpenAPISecuritySchemeRepresentable.swift +++ b/Sources/FeatherOpenAPI/SecurityScheme/OpenAPISecuritySchemeRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPISecuritySchemeRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,12 +7,17 @@ import OpenAPIKit30 +/// A type that can produce an OpenAPI security scheme. public protocol OpenAPISecuritySchemeRepresentable { + /// Returns the OpenAPI security scheme representation. + /// - Returns: The OpenAPI security scheme. func openAPISecurityScheme() -> OpenAPI.SecurityScheme } extension OpenAPI.SecurityScheme: OpenAPISecuritySchemeRepresentable { - + + /// Returns `self` as an OpenAPI security scheme. + /// - Returns: The current security scheme value. public func openAPISecurityScheme() -> OpenAPI.SecurityScheme { self } diff --git a/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeRepresentable.swift b/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeRepresentable.swift index 09975bd..50481f9 100644 --- a/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeRepresentable.swift +++ b/Sources/FeatherOpenAPI/SecurityScheme/SecuritySchemeRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// SecuritySchemeRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -7,18 +7,22 @@ import OpenAPIKit30 +/// Describes an OpenAPI security scheme. public protocol SecuritySchemeRepresentable: OpenAPISecuritySchemeRepresentable, Identifiable, DescriptionProperty, VendorExtensionsProperty { + /// The security scheme type. var type: OpenAPI.SecurityScheme.SecurityType { get } } -public extension SecuritySchemeRepresentable { - - func openAPISecurityScheme() -> OpenAPI.SecurityScheme { +extension SecuritySchemeRepresentable { + + /// Builds an OpenAPI security scheme. + /// - Returns: The OpenAPI security scheme. + public func openAPISecurityScheme() -> OpenAPI.SecurityScheme { .init( type: type, description: description, diff --git a/Sources/FeatherOpenAPI/Server/OpenAPIServerRepresentable.swift b/Sources/FeatherOpenAPI/Server/OpenAPIServerRepresentable.swift index 0cc8fc0..e29b800 100644 --- a/Sources/FeatherOpenAPI/Server/OpenAPIServerRepresentable.swift +++ b/Sources/FeatherOpenAPI/Server/OpenAPIServerRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPIServerRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,12 +7,17 @@ import OpenAPIKit30 +/// A type that can produce an OpenAPI server. public protocol OpenAPIServerRepresentable { + /// Returns the OpenAPI server representation. + /// - Returns: The OpenAPI server. func openAPIServer() -> OpenAPI.Server } extension OpenAPI.Server: OpenAPIServerRepresentable { - + + /// Returns `self` as an OpenAPI server. + /// - Returns: The current server value. public func openAPIServer() -> OpenAPI.Server { self } diff --git a/Sources/FeatherOpenAPI/Server/ServerRepresentable.swift b/Sources/FeatherOpenAPI/Server/ServerRepresentable.swift index 015fffa..f7c108a 100644 --- a/Sources/FeatherOpenAPI/Server/ServerRepresentable.swift +++ b/Sources/FeatherOpenAPI/Server/ServerRepresentable.swift @@ -1,5 +1,5 @@ // -// File 2.swift +// ServerRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -7,23 +7,29 @@ import OpenAPIKit30 +/// Describes an OpenAPI server object with defaults. public protocol ServerRepresentable: OpenAPIServerRepresentable, DescriptionProperty, VendorExtensionsProperty { + /// Server URL template. var url: LocationRepresentable { get } - + + /// Server variable definitions. var variables: VariableMap { get } - + } -public extension ServerRepresentable { +extension ServerRepresentable { + + /// Default server variables map. + public var variables: VariableMap { .init() } - var variables: VariableMap { .init() } - - func openAPIServer() -> OpenAPI.Server { + /// Builds an OpenAPI server object. + /// - Returns: The OpenAPI server. + public func openAPIServer() -> OpenAPI.Server { .init( url: url.openAPILocation(), description: description, diff --git a/Sources/FeatherOpenAPI/Tag/OpenAPITagRepresentable.swift b/Sources/FeatherOpenAPI/Tag/OpenAPITagRepresentable.swift index 6fefb57..387c83f 100644 --- a/Sources/FeatherOpenAPI/Tag/OpenAPITagRepresentable.swift +++ b/Sources/FeatherOpenAPI/Tag/OpenAPITagRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPITagRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -7,12 +7,17 @@ import OpenAPIKit30 +/// A type that can produce an OpenAPI tag. public protocol OpenAPITagRepresentable { + /// Returns the OpenAPI tag representation. + /// - Returns: The OpenAPI tag. func openAPITag() -> OpenAPI.Tag } extension OpenAPI.Tag: OpenAPITagRepresentable { + /// Returns `self` as an OpenAPI tag. + /// - Returns: The current tag value. public func openAPITag() -> OpenAPI.Tag { self } diff --git a/Sources/FeatherOpenAPI/Tag/TagRepresentable.swift b/Sources/FeatherOpenAPI/Tag/TagRepresentable.swift index 719cb27..44530dc 100644 --- a/Sources/FeatherOpenAPI/Tag/TagRepresentable.swift +++ b/Sources/FeatherOpenAPI/Tag/TagRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// TagRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,21 +7,27 @@ import OpenAPIKit30 +/// Describes an OpenAPI tag with defaults. public protocol TagRepresentable: OpenAPITagRepresentable, Identifiable, DescriptionProperty, VendorExtensionsProperty { + /// Tag display name. var name: String { get } + /// External documentation for the tag. var externalDocs: ExternalDocsRepresentable? { get } } -public extension TagRepresentable { - - var externalDocs: ExternalDocsRepresentable? { nil } +extension TagRepresentable { - func openAPITag() -> OpenAPI.Tag { + /// Default external docs is `nil`. + public var externalDocs: ExternalDocsRepresentable? { nil } + + /// Builds an OpenAPI tag object. + /// - Returns: The OpenAPI tag. + public func openAPITag() -> OpenAPI.Tag { .init( name: name, description: description, @@ -30,4 +36,3 @@ public extension TagRepresentable { ) } } - diff --git a/Sources/FeatherOpenAPI/Variable/OpenAPIVariableRepresentable.swift b/Sources/FeatherOpenAPI/Variable/OpenAPIVariableRepresentable.swift index 501cba2..e279064 100644 --- a/Sources/FeatherOpenAPI/Variable/OpenAPIVariableRepresentable.swift +++ b/Sources/FeatherOpenAPI/Variable/OpenAPIVariableRepresentable.swift @@ -1,5 +1,5 @@ // -// File.swift +// OpenAPIVariableRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,12 +7,17 @@ import OpenAPIKit30 +/// A type that can produce an OpenAPI server variable. public protocol OpenAPIVariableRepresentable { + /// Returns the OpenAPI server variable representation. + /// - Returns: The OpenAPI server variable. func openAPIServerVariable() -> OpenAPI.Server.Variable } extension OpenAPI.Server.Variable: OpenAPIVariableRepresentable { - + + /// Returns `self` as an OpenAPI server variable. + /// - Returns: The current server variable value. public func openAPIServerVariable() -> OpenAPI.Server.Variable { self } diff --git a/Sources/FeatherOpenAPI/Variable/VariableMap.swift b/Sources/FeatherOpenAPI/Variable/VariableMap.swift index a6be365..865b640 100644 --- a/Sources/FeatherOpenAPI/Variable/VariableMap.swift +++ b/Sources/FeatherOpenAPI/Variable/VariableMap.swift @@ -1,5 +1,5 @@ // -// File.swift +// VariableMap.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,6 +7,7 @@ import OpenAPIKit30 +/// Ordered map of server variable names to variable definitions. public typealias VariableMap = OrderedDictionary< String, OpenAPIVariableRepresentable diff --git a/Sources/FeatherOpenAPI/Variable/VariableRepresentable.swift b/Sources/FeatherOpenAPI/Variable/VariableRepresentable.swift index 834fce4..c5aa7a3 100644 --- a/Sources/FeatherOpenAPI/Variable/VariableRepresentable.swift +++ b/Sources/FeatherOpenAPI/Variable/VariableRepresentable.swift @@ -1,5 +1,5 @@ // -// File 2.swift +// VariableRepresentable.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -7,18 +7,23 @@ import OpenAPIKit30 +/// Describes an OpenAPI server variable. public protocol VariableRepresentable: OpenAPIVariableRepresentable, DescriptionProperty, VendorExtensionsProperty { + /// Allowed values for the variable. var `enum`: [String] { get } + /// Default value for the variable. var `default`: String { get } } -public extension VariableRepresentable { +extension VariableRepresentable { - func openAPIServerVariable() -> OpenAPI.Server.Variable { + /// Builds an OpenAPI server variable. + /// - Returns: The OpenAPI server variable. + public func openAPIServerVariable() -> OpenAPI.Server.Variable { .init( enum: `enum`, default: `default`, diff --git a/Sources/FeatherOpenAPI/_Properties/AllowedValuesProperty.swift b/Sources/FeatherOpenAPI/_Properties/AllowedValuesProperty.swift index 0153485..3b3d953 100644 --- a/Sources/FeatherOpenAPI/_Properties/AllowedValuesProperty.swift +++ b/Sources/FeatherOpenAPI/_Properties/AllowedValuesProperty.swift @@ -1,5 +1,5 @@ // -// File.swift +// AllowedValuesProperty.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,12 +7,16 @@ import OpenAPIKit30 +/// Provides allowed values for schemas. public protocol AllowedValuesProperty { + /// The associated allowed value type. associatedtype AllowedValuesPropertyType = AnyCodable - + + /// Allowed values for the schema. var allowedValues: [AllowedValuesPropertyType]? { get } } -public extension AllowedValuesProperty { - var allowedValues: [AllowedValuesPropertyType]? { nil } +extension AllowedValuesProperty { + /// Default allowed values are `nil`. + public var allowedValues: [AllowedValuesPropertyType]? { nil } } diff --git a/Sources/FeatherOpenAPI/_Properties/DefaultValueProperty.swift b/Sources/FeatherOpenAPI/_Properties/DefaultValueProperty.swift index 234b390..b56154f 100644 --- a/Sources/FeatherOpenAPI/_Properties/DefaultValueProperty.swift +++ b/Sources/FeatherOpenAPI/_Properties/DefaultValueProperty.swift @@ -1,5 +1,5 @@ // -// File.swift +// DefaultValueProperty.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,12 +7,16 @@ import OpenAPIKit30 +/// Provides a default value for schemas. public protocol DefaultValueProperty { + /// The associated default value type. associatedtype DefaultValuePropertyType = AnyCodable - + + /// The default value. var defaultValue: DefaultValuePropertyType? { get } } -public extension DefaultValueProperty { - var defaultValue: DefaultValuePropertyType? { nil } +extension DefaultValueProperty { + /// Default default value is `nil`. + public var defaultValue: DefaultValuePropertyType? { nil } } diff --git a/Sources/FeatherOpenAPI/_Properties/DeprecatedProperty.swift b/Sources/FeatherOpenAPI/_Properties/DeprecatedProperty.swift index 8b24a9b..73fa6e6 100644 --- a/Sources/FeatherOpenAPI/_Properties/DeprecatedProperty.swift +++ b/Sources/FeatherOpenAPI/_Properties/DeprecatedProperty.swift @@ -1,14 +1,17 @@ // -// File.swift +// DeprecatedProperty.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. // +/// Provides a deprecated flag with a default value. public protocol DeprecatedProperty { + /// Indicates whether the item is deprecated. var deprecated: Bool { get } } -public extension DeprecatedProperty { - var deprecated: Bool { false } +extension DeprecatedProperty { + /// Default deprecated value is `false`. + public var deprecated: Bool { false } } diff --git a/Sources/FeatherOpenAPI/_Properties/DescriptionProperty.swift b/Sources/FeatherOpenAPI/_Properties/DescriptionProperty.swift index 143b394..bdb6b0d 100644 --- a/Sources/FeatherOpenAPI/_Properties/DescriptionProperty.swift +++ b/Sources/FeatherOpenAPI/_Properties/DescriptionProperty.swift @@ -1,14 +1,17 @@ // -// File.swift +// DescriptionProperty.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. // +/// Provides a description property with a default value. public protocol DescriptionProperty { + /// Human-readable description. var description: String? { get } } -public extension DescriptionProperty { - var description: String? { nil } +extension DescriptionProperty { + /// Default description is `nil`. + public var description: String? { nil } } diff --git a/Sources/FeatherOpenAPI/_Properties/ExampleProperty.swift b/Sources/FeatherOpenAPI/_Properties/ExampleProperty.swift index 7ea46b6..517fcf2 100644 --- a/Sources/FeatherOpenAPI/_Properties/ExampleProperty.swift +++ b/Sources/FeatherOpenAPI/_Properties/ExampleProperty.swift @@ -1,5 +1,5 @@ // -// File.swift +// ExampleProperty.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -7,12 +7,16 @@ import OpenAPIKit30 +/// Provides an example value for schemas. public protocol ExampleProperty { + /// The associated example value type. associatedtype ExamplePropertyType = AnyCodable - + + /// The example value. var example: ExamplePropertyType? { get } } -public extension ExampleProperty { - var example: ExamplePropertyType? { nil } +extension ExampleProperty { + /// Default example value is `nil`. + public var example: ExamplePropertyType? { nil } } diff --git a/Sources/FeatherOpenAPI/_Properties/NullableProperty.swift b/Sources/FeatherOpenAPI/_Properties/NullableProperty.swift index 97cfc32..82d5814 100644 --- a/Sources/FeatherOpenAPI/_Properties/NullableProperty.swift +++ b/Sources/FeatherOpenAPI/_Properties/NullableProperty.swift @@ -1,15 +1,17 @@ // -// File.swift +// NullableProperty.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. // - +/// Provides a nullable flag with a default value. public protocol NullableProperty { + /// Indicates whether the schema value may be null. var nullable: Bool? { get } } -public extension NullableProperty { - var nullable: Bool? { nil } +extension NullableProperty { + /// Default nullable value is `nil`. + public var nullable: Bool? { nil } } diff --git a/Sources/FeatherOpenAPI/_Properties/RequiredProperty.swift b/Sources/FeatherOpenAPI/_Properties/RequiredProperty.swift index fa14aae..653dec5 100644 --- a/Sources/FeatherOpenAPI/_Properties/RequiredProperty.swift +++ b/Sources/FeatherOpenAPI/_Properties/RequiredProperty.swift @@ -1,14 +1,17 @@ // -// File.swift +// RequiredProperty.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. // +/// Provides a `required` flag with a default value. public protocol RequiredProperty { + /// Indicates whether the item is required. var required: Bool { get } } -public extension RequiredProperty { - var required: Bool { true } +extension RequiredProperty { + /// Default required value is `true`. + public var required: Bool { true } } diff --git a/Sources/FeatherOpenAPI/_Properties/TitleProperty.swift b/Sources/FeatherOpenAPI/_Properties/TitleProperty.swift index 392f826..436d2f5 100644 --- a/Sources/FeatherOpenAPI/_Properties/TitleProperty.swift +++ b/Sources/FeatherOpenAPI/_Properties/TitleProperty.swift @@ -1,14 +1,17 @@ // -// File.swift +// TitleProperty.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. // +/// Provides a title property with a default value. public protocol TitleProperty { + /// Short title. var title: String? { get } } -public extension TitleProperty { - var title: String? { nil } +extension TitleProperty { + /// Default title is `nil`. + public var title: String? { nil } } diff --git a/Sources/FeatherOpenAPI/_Properties/VendorExtensionsProperty.swift b/Sources/FeatherOpenAPI/_Properties/VendorExtensionsProperty.swift index e29ad2d..66c0cf4 100644 --- a/Sources/FeatherOpenAPI/_Properties/VendorExtensionsProperty.swift +++ b/Sources/FeatherOpenAPI/_Properties/VendorExtensionsProperty.swift @@ -1,5 +1,5 @@ // -// File.swift +// VendorExtensionsProperty.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 23.. @@ -7,12 +7,13 @@ import OpenAPIKit30 +/// Provides vendor extension storage. public protocol VendorExtensionsProperty { + /// Vendor extension values keyed by extension name. var vendorExtensions: [String: AnyCodable] { get } } -public extension VendorExtensionsProperty { - var vendorExtensions: [String: AnyCodable] { [:] } +extension VendorExtensionsProperty { + /// Default vendor extensions are empty. + public var vendorExtensions: [String: AnyCodable] { [:] } } - - diff --git a/Tests/FeatherOpenAPITests/Example/Example.swift b/Tests/FeatherOpenAPITests/Example/Example.swift index d023f26..b33805c 100644 --- a/Tests/FeatherOpenAPITests/Example/Example.swift +++ b/Tests/FeatherOpenAPITests/Example/Example.swift @@ -1,3 +1,9 @@ +// +// Example.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift index 0cb05df..3b5f230 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift @@ -1,3 +1,9 @@ +// +// ExampleDocument.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // @@ -48,12 +54,12 @@ struct ExamplePathCollection: PathCollectionRepresentable { } struct ExampleDocument: DocumentRepresentable { - + let collection = ExamplePathCollection() var info: OpenAPIInfoRepresentable { ExampleInfo() } var servers: [OpenAPIServerRepresentable] { [ExampleServer()] } var paths: PathMap { collection.pathMap } - var components: OpenAPIComponentsRepresentable { collection.components } + var components: OpenAPIComponentsRepresentable { collection.components } } diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItem.swift b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItem.swift index 5e23ba9..037a9f6 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItem.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItem.swift @@ -1,3 +1,9 @@ +// +// ExampleDuplicatedItem.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift index d3ce66a..853ded6 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift @@ -1,3 +1,9 @@ +// +// ExampleDuplicatedItemDocument.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // @@ -28,7 +34,9 @@ struct ExampleDuplicatedItemServer: ServerRepresentable { struct ExampleDuplicatedItemDocument: DocumentRepresentable { var info: OpenAPIInfoRepresentable { ExampleDuplicatedItemInfo() } - var servers: [OpenAPIServerRepresentable] { [ExampleDuplicatedItemServer()] } + var servers: [OpenAPIServerRepresentable] { + [ExampleDuplicatedItemServer()] + } var paths: PathMap { [:] } var components: OpenAPIComponentsRepresentable { let idSchema = ExampleDuplicatedItem.Model.IdSchema() diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel+Schemas.swift index 0fcd9e2..43914d9 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel+Schemas.swift @@ -1,3 +1,9 @@ +// +// ExampleDuplicatedItemModel+Schemas.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel.swift b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel.swift index 2c8229e..d3f9941 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel.swift @@ -1,3 +1,9 @@ +// +// ExampleDuplicatedItemModel.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // diff --git a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItem.swift b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItem.swift index 2cec5f7..380b437 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItem.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItem.swift @@ -1,3 +1,9 @@ +// +// ExampleMissingParentItem.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // diff --git a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift index ba1f01a..d0f2d68 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift @@ -1,3 +1,9 @@ +// +// ExampleMissingParentItemDocument.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // @@ -28,7 +34,9 @@ struct ExampleMissingParentItemServer: ServerRepresentable { struct ExampleMissingParentItemItemDocument: DocumentRepresentable { var info: OpenAPIInfoRepresentable { ExampleMissingParentItemInfo() } - var servers: [OpenAPIServerRepresentable] { [ExampleMissingParentItemServer()] } + var servers: [OpenAPIServerRepresentable] { + [ExampleMissingParentItemServer()] + } var paths: PathMap { [:] } var components: OpenAPIComponentsRepresentable { let idSchema = ExampleMissingParentItem.Model.IdSchema() diff --git a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel+Schemas.swift index 0bfbf7c..c0a8423 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel+Schemas.swift @@ -1,3 +1,9 @@ +// +// ExampleMissingParentItemModel+Schemas.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // diff --git a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel.swift b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel.swift index 23d63c7..c973673 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel.swift @@ -1,3 +1,9 @@ +// +// ExampleMissingParentItemModel.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+Headers.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Headers.swift index 4dd7fbe..76f8caa 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+Headers.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Headers.swift @@ -1,3 +1,9 @@ +// +// ExampleModel+Headers.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+Operations.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Operations.swift index e555d9c..cec899d 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+Operations.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Operations.swift @@ -1,3 +1,9 @@ +// +// ExampleModel+Operations.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // @@ -17,7 +23,7 @@ extension Example.Model { var parameters: [ParameterRepresentable] { [ IdParameter().reference(), - CustomRequestHeaderParameter().reference() + CustomRequestHeaderParameter().reference(), ] } var responseMap: ResponseMap { diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+Parameters.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Parameters.swift index bc56e03..ae21196 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+Parameters.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Parameters.swift @@ -1,3 +1,9 @@ +// +// ExampleModel+Parameters.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+PathItems.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+PathItems.swift index 288f079..b616d70 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+PathItems.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+PathItems.swift @@ -1,3 +1,9 @@ +// +// ExampleModel+PathItems.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+RequestBodies.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+RequestBodies.swift index 4a1e962..fd3fa4d 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+RequestBodies.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+RequestBodies.swift @@ -1,3 +1,9 @@ +// +// ExampleModel+RequestBodies.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+Responses.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Responses.swift index 646d109..91d03be 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+Responses.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Responses.swift @@ -1,3 +1,9 @@ +// +// ExampleModel+Responses.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Schemas.swift index a7885a8..5a0f176 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Schemas.swift @@ -1,3 +1,9 @@ +// +// ExampleModel+Schemas.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+SecuritySchemes.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+SecuritySchemes.swift index 9f5ab66..256ceac 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+SecuritySchemes.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+SecuritySchemes.swift @@ -1,3 +1,9 @@ +// +// ExampleModel+SecuritySchemes.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+Tags.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Tags.swift index ac83491..18a0d85 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+Tags.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Tags.swift @@ -1,3 +1,9 @@ +// +// ExampleModel+Tags.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel.swift index b21731d..2e689c1 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel.swift @@ -1,3 +1,9 @@ +// +// ExampleModel.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // diff --git a/Tests/FeatherOpenAPITests/ExampleTestSuite.swift b/Tests/FeatherOpenAPITests/ExampleTestSuite.swift index e81a128..bedda2f 100644 --- a/Tests/FeatherOpenAPITests/ExampleTestSuite.swift +++ b/Tests/FeatherOpenAPITests/ExampleTestSuite.swift @@ -1,3 +1,9 @@ +// +// ExampleTestSuite.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // @@ -9,13 +15,11 @@ import Foundation import OpenAPIKit import OpenAPIKit30 import OpenAPIKitCompat -import Yams import Testing +import Yams @testable import FeatherOpenAPI - - @Suite struct ExampleTestSuite { @@ -26,7 +30,7 @@ struct ExampleTestSuite { let openAPIdoc = document.openAPIDocument() _ = try openAPIdoc.locallyDereferenced().resolved() - + let encoder = YAMLEncoder() let result = try encoder.encode(openAPIdoc) print("---- 3.0 ----") @@ -37,10 +41,10 @@ struct ExampleTestSuite { func duplicatedItem() throws { let document = ExampleDuplicatedItemDocument() - + let openAPIdoc = document.openAPIDocument() _ = try openAPIdoc.locallyDereferenced().resolved() - + let encoder = YAMLEncoder() let result = try encoder.encode(openAPIdoc) print("---- 3.0 ----") @@ -54,7 +58,7 @@ struct ExampleTestSuite { let openAPIdoc = document.openAPIDocument() _ = try openAPIdoc.locallyDereferenced().resolved() - + let encoder = YAMLEncoder() let result = try encoder.encode(openAPIdoc) print("---- 3.0 ----") diff --git a/Tests/FeatherOpenAPIKitTests/PathComponentTests.swift b/Tests/FeatherOpenAPITests/PathComponentTests.swift similarity index 89% rename from Tests/FeatherOpenAPIKitTests/PathComponentTests.swift rename to Tests/FeatherOpenAPITests/PathComponentTests.swift index 394e8f7..e61f0d7 100644 --- a/Tests/FeatherOpenAPIKitTests/PathComponentTests.swift +++ b/Tests/FeatherOpenAPITests/PathComponentTests.swift @@ -1,3 +1,9 @@ +// +// PathComponentTests.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 22.. + // // File.swift // @@ -10,13 +16,13 @@ import Testing @testable import FeatherOpenAPIKit -fileprivate extension Path { +extension Path { - static func star(_ param: String) -> Path { + fileprivate static func star(_ param: String) -> Path { Path("*" + param + "*") } - static func superstar() -> Path { + fileprivate static func superstar() -> Path { Path("********") } } @@ -26,9 +32,9 @@ private struct ParameterDummy { let name: String } -fileprivate extension Path { +extension Path { - static func parameter(_ param: ParameterDummy) -> Path { + fileprivate static func parameter(_ param: ParameterDummy) -> Path { parameter(param.name) } } diff --git a/Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse+Schemas.swift b/Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse+Schemas.swift index 156c18d..6e85b4c 100644 --- a/Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse+Schemas.swift @@ -1,5 +1,5 @@ // -// File.swift +// ApiResponse+Schemas.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. diff --git a/Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse.swift b/Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse.swift index 9ec90c1..1939df3 100644 --- a/Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse.swift +++ b/Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse.swift @@ -1,5 +1,5 @@ // -// File.swift +// ApiResponse.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. diff --git a/Tests/FeatherOpenAPITests/Petstore/Category/Category+Schemas.swift b/Tests/FeatherOpenAPITests/Petstore/Category/Category+Schemas.swift index 121c4c0..7b9f500 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Category/Category+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Category/Category+Schemas.swift @@ -1,5 +1,5 @@ // -// File.swift +// Category+Schemas.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. diff --git a/Tests/FeatherOpenAPITests/Petstore/Category/Category.swift b/Tests/FeatherOpenAPITests/Petstore/Category/Category.swift index 12374dd..91849fc 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Category/Category.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Category/Category.swift @@ -1,5 +1,5 @@ // -// File.swift +// Category.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Operations.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Operations.swift index 0682d39..ddd7fd5 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Operations.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Operations.swift @@ -1,5 +1,5 @@ // -// File.swift +// Pet+Operations.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -17,12 +17,12 @@ extension Petstore.Pet { var operationId: String? { "updatePet" } var parameters: [ParameterRepresentable] { [ - IdParameter().reference(), + IdParameter().reference() ] } var responseMap: ResponseMap { [ - 200: DetailResponse().reference(), + 200: DetailResponse().reference() ] } } diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Parameters.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Parameters.swift index 2eb3463..37075cf 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Parameters.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Parameters.swift @@ -1,5 +1,5 @@ // -// File.swift +// Pet+Parameters.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+PathItems.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+PathItems.swift index fef6a82..54ac695 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+PathItems.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+PathItems.swift @@ -1,5 +1,5 @@ // -// File.swift +// Pet+PathItems.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Responses.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Responses.swift index 1112d95..88eac3a 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Responses.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Responses.swift @@ -1,5 +1,5 @@ // -// File.swift +// Pet+Responses.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Schemas.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Schemas.swift index afb8989..180b8be 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Schemas.swift @@ -1,5 +1,5 @@ // -// File.swift +// Pet+Schemas.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -46,7 +46,8 @@ extension Petstore.Pet { [ "id": IdSchema().reference(required: false), "name": NameSchema(), - "category": Petstore.Category.CategorySchema().reference(required: false), + "category": Petstore.Category.CategorySchema() + .reference(required: false), "photoUrls": PhotoUrlsSchema(), "tags": TagsSchema(), "status": StatusSchema().reference(required: false), diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Tags.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Tags.swift index 4da45db..638027e 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Tags.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Tags.swift @@ -1,5 +1,5 @@ // -// File.swift +// Pet+Tags.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet.swift index 3526038..63d77ea 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet.swift @@ -1,5 +1,5 @@ // -// File.swift +// Pet.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. diff --git a/Tests/FeatherOpenAPITests/Petstore/Petstore.swift b/Tests/FeatherOpenAPITests/Petstore/Petstore.swift index dc9c033..743f908 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Petstore.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Petstore.swift @@ -1,5 +1,5 @@ // -// File.swift +// Petstore.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -21,7 +21,9 @@ struct PetstoreContact: ContactRepresentable { struct PetstoreLicense: LicenseRepresentable { var name: String { "Apache 2.0" } var url: LocationRepresentable? { - PetstoreLocation(location: "https://www.apache.org/licenses/LICENSE-2.0.html") + PetstoreLocation( + location: "https://www.apache.org/licenses/LICENSE-2.0.html" + ) } } diff --git a/Tests/FeatherOpenAPITests/Petstore/Store/Store+Schemas.swift b/Tests/FeatherOpenAPITests/Petstore/Store/Store+Schemas.swift index ee0ab57..7fa3c25 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Store/Store+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Store/Store+Schemas.swift @@ -1,5 +1,5 @@ // -// File.swift +// Store+Schemas.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. diff --git a/Tests/FeatherOpenAPITests/Petstore/Store/Store.swift b/Tests/FeatherOpenAPITests/Petstore/Store/Store.swift index 3bd691d..38c8164 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Store/Store.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Store/Store.swift @@ -1,5 +1,5 @@ // -// File.swift +// Store.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. diff --git a/Tests/FeatherOpenAPITests/Petstore/Tag/Tag+Schemas.swift b/Tests/FeatherOpenAPITests/Petstore/Tag/Tag+Schemas.swift index 44db974..d253e2d 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Tag/Tag+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Tag/Tag+Schemas.swift @@ -1,5 +1,5 @@ // -// File.swift +// Tag+Schemas.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. diff --git a/Tests/FeatherOpenAPITests/Petstore/Tag/Tag.swift b/Tests/FeatherOpenAPITests/Petstore/Tag/Tag.swift index 0278d68..2432e9c 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Tag/Tag.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Tag/Tag.swift @@ -1,5 +1,5 @@ // -// File.swift +// Tag.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. diff --git a/Tests/FeatherOpenAPITests/Petstore/User/User+Schemas.swift b/Tests/FeatherOpenAPITests/Petstore/User/User+Schemas.swift index 298328b..5cee0b2 100644 --- a/Tests/FeatherOpenAPITests/Petstore/User/User+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Petstore/User/User+Schemas.swift @@ -1,5 +1,5 @@ // -// File.swift +// User+Schemas.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. diff --git a/Tests/FeatherOpenAPITests/Petstore/User/User.swift b/Tests/FeatherOpenAPITests/Petstore/User/User.swift index 1d048b4..110334a 100644 --- a/Tests/FeatherOpenAPITests/Petstore/User/User.swift +++ b/Tests/FeatherOpenAPITests/Petstore/User/User.swift @@ -1,5 +1,5 @@ // -// File.swift +// User.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. diff --git a/Tests/FeatherOpenAPITests/PetstoreTestSuite.swift b/Tests/FeatherOpenAPITests/PetstoreTestSuite.swift index 3683a87..00c61e7 100644 --- a/Tests/FeatherOpenAPITests/PetstoreTestSuite.swift +++ b/Tests/FeatherOpenAPITests/PetstoreTestSuite.swift @@ -1,5 +1,5 @@ // -// File.swift +// PetstoreTestSuite.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 22.. @@ -8,8 +8,8 @@ import Foundation import OpenAPIKit30 import OpenAPIKitCore -import Yams import Testing +import Yams @testable import FeatherOpenAPI diff --git a/Tests/FeatherOpenAPITests/Todo/TestObjects.swift b/Tests/FeatherOpenAPITests/Todo/TestObjects.swift index 8916acc..6eaf712 100644 --- a/Tests/FeatherOpenAPITests/Todo/TestObjects.swift +++ b/Tests/FeatherOpenAPITests/Todo/TestObjects.swift @@ -1,5 +1,5 @@ // -// File.swift +// TestObjects.swift // feather-openapi // // Created by Tibor Bödecs on 2026. 01. 21.. @@ -9,12 +9,11 @@ import FeatherOpenAPI import OpenAPIKit30 struct MyPathCollection: PathCollectionRepresentable { - - + var pathMap: PathMap { [ - "todos": TodoPathItems(), -// "laci": LaciPathItems(), + "todos": TodoPathItems() + // "laci": LaciPathItems(), ] } } @@ -27,16 +26,16 @@ struct MyInfo: InfoRepresentable { struct MyDocument: DocumentRepresentable { var info: OpenAPIInfoRepresentable - + var servers: [any OpenAPIServerRepresentable] { [ TestServer() ] } - + var paths: PathMap var components: OpenAPIComponentsRepresentable - + init( info: OpenAPIInfoRepresentable, paths: PathMap, @@ -56,7 +55,6 @@ struct TestServer: ServerRepresentable { var url: any LocationRepresentable { "http://127.0.0.1:8080/" } } - struct TodoIDField: IntSchemaRepresentable { var example: Int? { 1 } } @@ -70,23 +68,23 @@ struct TodoIsCompleteField: BoolSchemaRepresentable { } struct TodoDetailObject: ObjectSchemaRepresentable { - + var propertyMap: SchemaMap { [ "id": TodoIDField().reference(), "title": TodoTitleField(), "isComplete": TodoIsCompleteField(), -// "unsafe": UnsafeSchemaReference("asdf"), + // "unsafe": UnsafeSchemaReference("asdf"), ] } } struct TodoCreateRequestBody: RequestBodyRepresentable { - + var contentMap: ContentMap { [ - .json: Content(TodoDetailObject().reference()), + .json: Content(TodoDetailObject().reference()) ] } } @@ -103,7 +101,7 @@ struct TodoCreateResponse: JSONResponseRepresentable { } struct TodoIdParameter: ParameterRepresentable { - + var name: String { "todoId" } var context: OpenAPIKit30.OpenAPI.Parameter.Context { .path @@ -134,7 +132,7 @@ struct TodoCreateOperation: OperationRepresentable { var parameters: [ParameterRepresentable] { [ - TodoIdParameter().reference(), + TodoIdParameter().reference() ] } @@ -144,17 +142,17 @@ struct TodoCreateOperation: OperationRepresentable { var responseMap: ResponseMap { [ - 200: TodoCreateResponse().reference(), + 200: TodoCreateResponse().reference() ] } - + var security: [any SecurityRequirementRepresentable]? { [ OAuthSecurityRequirement(), APIKeySecurityRequirement(), ] } - + var servers: [any ServerRepresentable]? { [ TestServer() @@ -162,9 +160,6 @@ struct TodoCreateOperation: OperationRepresentable { } } - - - struct TodoPathItems: PathItemRepresentable { var post: OperationRepresentable? = TodoCreateOperation() } @@ -177,7 +172,7 @@ struct OAuthSecurityScheme: SecuritySchemeRepresentable { } struct OAuthSecurityRequirement: SecurityRequirementRepresentable { - + var security: any SecuritySchemeRepresentable = OAuthSecurityScheme() var requirements: [String] = ["read"] } diff --git a/Tests/FeatherOpenAPITests/TodoTestSuite.swift b/Tests/FeatherOpenAPITests/TodoTestSuite.swift index 1ec5fce..29bfb44 100644 --- a/Tests/FeatherOpenAPITests/TodoTestSuite.swift +++ b/Tests/FeatherOpenAPITests/TodoTestSuite.swift @@ -1,3 +1,9 @@ +// +// TodoTestSuite.swift +// feather-openapi +// +// Created by Tibor Bodecs on 2026. 01. 25.. + // // File.swift // @@ -9,24 +15,20 @@ import Foundation import OpenAPIKit import OpenAPIKit30 import OpenAPIKitCompat -import Yams import Testing +import Yams @testable import FeatherOpenAPI - - @Suite struct TodoTestSuite { - + @Test func example() throws { - - let collection = MyPathCollection() -// collection.components.schemas.register(id: "", TodoFieldId()) - + // collection.components.schemas.register(id: "", TodoFieldId()) + let document = MyDocument( info: MyInfo(), paths: collection.pathMap, @@ -37,7 +39,8 @@ struct TodoTestSuite { let encoder = YAMLEncoder() - _ = try openAPIdoc + _ = + try openAPIdoc .locallyDereferenced() .resolved() @@ -45,15 +48,15 @@ struct TodoTestSuite { print("---- 3.0 ----") print(result) -// let doc31 = openAPIdoc.convert(to: .v3_1_0) -// let result31 = try encoder.encode(doc31) -// print("---- 3.1 ----") -// print(result31) -// -// let doc32 = openAPIdoc.convert(to: .v3_2_0) -// let result32 = try encoder.encode(doc32) -// print("---- 3.2 ----") -// print(result32) + // let doc31 = openAPIdoc.convert(to: .v3_1_0) + // let result31 = try encoder.encode(doc31) + // print("---- 3.1 ----") + // print(result31) + // + // let doc32 = openAPIdoc.convert(to: .v3_2_0) + // let result32 = try encoder.encode(doc32) + // print("---- 3.2 ----") + // print(result32) } } From 03a66ea072959b02c8b89fcf9269e77435decf26 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sun, 25 Jan 2026 20:50:58 +0100 Subject: [PATCH 30/34] fix headers & tests --- Sources/FeatherOpenAPI/Callback/CallbackID.swift | 1 - Sources/FeatherOpenAPI/Example/ExampleID.swift | 1 - Sources/FeatherOpenAPI/Header/HeaderID.swift | 1 - Sources/FeatherOpenAPI/Identifiable.swift | 1 - Sources/FeatherOpenAPI/Link/LinkID.swift | 1 - Sources/FeatherOpenAPI/OrderedDictionary+Merge.swift | 7 ------- .../Parameter/Abstraction/ParameterID.swift | 1 - Sources/FeatherOpenAPI/Path.swift | 7 ------- .../RequestBody/Abstraction/RequestBodyID.swift | 1 - .../FeatherOpenAPI/Response/Abstraction/ResponseID.swift | 1 - Sources/FeatherOpenAPI/Schema/Abstraction/SchemaID.swift | 1 - .../FeatherOpenAPI/_Properties/DeprecatedProperty.swift | 1 - .../FeatherOpenAPI/_Properties/DescriptionProperty.swift | 1 - .../FeatherOpenAPI/_Properties/NullableProperty.swift | 1 - .../FeatherOpenAPI/_Properties/RequiredProperty.swift | 1 - Sources/FeatherOpenAPI/_Properties/TitleProperty.swift | 1 - Tests/FeatherOpenAPITests/Example/Example.swift | 7 ------- Tests/FeatherOpenAPITests/Example/ExampleDocument.swift | 7 ------- .../Example/ExampleDuplicatedItem.swift | 7 ------- .../Example/ExampleDuplicatedItemDocument.swift | 7 ------- .../Example/ExampleDuplicatedItemModel+Schemas.swift | 7 ------- .../Example/ExampleDuplicatedItemModel.swift | 7 ------- .../Example/ExampleMissingParentItem.swift | 7 ------- .../Example/ExampleMissingParentItemDocument.swift | 7 ------- .../Example/ExampleMissingParentItemModel+Schemas.swift | 7 ------- .../Example/ExampleMissingParentItemModel.swift | 7 ------- .../Example/ExampleModel+Headers.swift | 7 ------- .../Example/ExampleModel+Operations.swift | 7 ------- .../Example/ExampleModel+Parameters.swift | 7 ------- .../Example/ExampleModel+PathItems.swift | 7 ------- .../Example/ExampleModel+RequestBodies.swift | 7 ------- .../Example/ExampleModel+Responses.swift | 7 ------- .../Example/ExampleModel+Schemas.swift | 7 ------- .../Example/ExampleModel+SecuritySchemes.swift | 7 ------- .../FeatherOpenAPITests/Example/ExampleModel+Tags.swift | 7 ------- Tests/FeatherOpenAPITests/Example/ExampleModel.swift | 7 ------- Tests/FeatherOpenAPITests/ExampleTestSuite.swift | 7 ------- Tests/FeatherOpenAPITests/PathComponentTests.swift | 9 +-------- Tests/FeatherOpenAPITests/TodoTestSuite.swift | 7 ------- 39 files changed, 1 insertion(+), 190 deletions(-) diff --git a/Sources/FeatherOpenAPI/Callback/CallbackID.swift b/Sources/FeatherOpenAPI/Callback/CallbackID.swift index fd45e4a..677c9dc 100644 --- a/Sources/FeatherOpenAPI/Callback/CallbackID.swift +++ b/Sources/FeatherOpenAPI/Callback/CallbackID.swift @@ -5,7 +5,6 @@ // Created by Tibor Bödecs on 2026. 01. 23.. // -/// Strongly typed identifier for reusable OpenAPI callbacks. public struct CallbackID: Sendable, Equatable, Hashable, Codable { /// The raw identifier value. diff --git a/Sources/FeatherOpenAPI/Example/ExampleID.swift b/Sources/FeatherOpenAPI/Example/ExampleID.swift index e7ebc26..8c776c3 100644 --- a/Sources/FeatherOpenAPI/Example/ExampleID.swift +++ b/Sources/FeatherOpenAPI/Example/ExampleID.swift @@ -5,7 +5,6 @@ // Created by Tibor Bödecs on 2026. 01. 23.. // -/// Strongly typed identifier for reusable OpenAPI examples. public struct ExampleID: Sendable, Equatable, Hashable, Codable { /// The raw identifier value. diff --git a/Sources/FeatherOpenAPI/Header/HeaderID.swift b/Sources/FeatherOpenAPI/Header/HeaderID.swift index 159b50e..5e58fcc 100644 --- a/Sources/FeatherOpenAPI/Header/HeaderID.swift +++ b/Sources/FeatherOpenAPI/Header/HeaderID.swift @@ -5,7 +5,6 @@ // Created by Tibor Bödecs on 2026. 01. 23.. // -/// Strongly typed identifier for reusable OpenAPI headers. public struct HeaderID: Sendable, Equatable, Hashable, Codable { /// The raw identifier value. diff --git a/Sources/FeatherOpenAPI/Identifiable.swift b/Sources/FeatherOpenAPI/Identifiable.swift index c8983e9..34e3902 100644 --- a/Sources/FeatherOpenAPI/Identifiable.swift +++ b/Sources/FeatherOpenAPI/Identifiable.swift @@ -5,7 +5,6 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -/// Provides a default OpenAPI identifier based on the type name. public protocol Identifiable: Sendable { /// The identifier to use for OpenAPI component references. var openAPIIdentifier: String { get } diff --git a/Sources/FeatherOpenAPI/Link/LinkID.swift b/Sources/FeatherOpenAPI/Link/LinkID.swift index 11c0b25..694c9cc 100644 --- a/Sources/FeatherOpenAPI/Link/LinkID.swift +++ b/Sources/FeatherOpenAPI/Link/LinkID.swift @@ -5,7 +5,6 @@ // Created by Tibor Bödecs on 2026. 01. 23.. // -/// Strongly typed identifier for reusable OpenAPI links. public struct LinkID: Sendable, Equatable, Hashable, Codable { /// The raw identifier value. diff --git a/Sources/FeatherOpenAPI/OrderedDictionary+Merge.swift b/Sources/FeatherOpenAPI/OrderedDictionary+Merge.swift index 0eef265..62e796f 100644 --- a/Sources/FeatherOpenAPI/OrderedDictionary+Merge.swift +++ b/Sources/FeatherOpenAPI/OrderedDictionary+Merge.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 10/01/2024. -// - import OpenAPIKit30 extension OrderedDictionary { diff --git a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift index 8d41ade..7521ec4 100644 --- a/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift +++ b/Sources/FeatherOpenAPI/Parameter/Abstraction/ParameterID.swift @@ -5,7 +5,6 @@ // Created by Tibor Bödecs on 2026. 01. 23.. // -/// Strongly typed identifier for reusable OpenAPI parameters. public struct ParameterID: Sendable, Equatable, Hashable, Codable { /// The raw identifier value. diff --git a/Sources/FeatherOpenAPI/Path.swift b/Sources/FeatherOpenAPI/Path.swift index 46d3cba..76c6f3d 100644 --- a/Sources/FeatherOpenAPI/Path.swift +++ b/Sources/FeatherOpenAPI/Path.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - /// A lightweight OpenAPI path wrapper with composition helpers. public struct Path: ExpressibleByStringLiteral { diff --git a/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyID.swift b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyID.swift index b47ba93..28aecf8 100644 --- a/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyID.swift +++ b/Sources/FeatherOpenAPI/RequestBody/Abstraction/RequestBodyID.swift @@ -5,7 +5,6 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -/// Strongly typed identifier for reusable OpenAPI request bodies. public struct RequestBodyID: Sendable, Equatable, Hashable, Codable { /// The raw identifier value. diff --git a/Sources/FeatherOpenAPI/Response/Abstraction/ResponseID.swift b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseID.swift index 8c8a663..6aae4bf 100644 --- a/Sources/FeatherOpenAPI/Response/Abstraction/ResponseID.swift +++ b/Sources/FeatherOpenAPI/Response/Abstraction/ResponseID.swift @@ -5,7 +5,6 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -/// Strongly typed identifier for reusable OpenAPI responses. public struct ResponseID: Sendable, Equatable, Hashable, Codable { /// The raw identifier value. diff --git a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaID.swift b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaID.swift index f269041..08c9133 100644 --- a/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaID.swift +++ b/Sources/FeatherOpenAPI/Schema/Abstraction/SchemaID.swift @@ -5,7 +5,6 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -/// Strongly typed identifier for reusable OpenAPI schemas. public struct SchemaID: Sendable, Equatable, Hashable, Codable { /// The raw identifier value. diff --git a/Sources/FeatherOpenAPI/_Properties/DeprecatedProperty.swift b/Sources/FeatherOpenAPI/_Properties/DeprecatedProperty.swift index 73fa6e6..e14bd7f 100644 --- a/Sources/FeatherOpenAPI/_Properties/DeprecatedProperty.swift +++ b/Sources/FeatherOpenAPI/_Properties/DeprecatedProperty.swift @@ -5,7 +5,6 @@ // Created by Tibor Bödecs on 2026. 01. 23.. // -/// Provides a deprecated flag with a default value. public protocol DeprecatedProperty { /// Indicates whether the item is deprecated. var deprecated: Bool { get } diff --git a/Sources/FeatherOpenAPI/_Properties/DescriptionProperty.swift b/Sources/FeatherOpenAPI/_Properties/DescriptionProperty.swift index bdb6b0d..3e9a7a9 100644 --- a/Sources/FeatherOpenAPI/_Properties/DescriptionProperty.swift +++ b/Sources/FeatherOpenAPI/_Properties/DescriptionProperty.swift @@ -5,7 +5,6 @@ // Created by Tibor Bödecs on 2026. 01. 23.. // -/// Provides a description property with a default value. public protocol DescriptionProperty { /// Human-readable description. var description: String? { get } diff --git a/Sources/FeatherOpenAPI/_Properties/NullableProperty.swift b/Sources/FeatherOpenAPI/_Properties/NullableProperty.swift index 82d5814..ca66936 100644 --- a/Sources/FeatherOpenAPI/_Properties/NullableProperty.swift +++ b/Sources/FeatherOpenAPI/_Properties/NullableProperty.swift @@ -5,7 +5,6 @@ // Created by Tibor Bödecs on 2026. 01. 23.. // -/// Provides a nullable flag with a default value. public protocol NullableProperty { /// Indicates whether the schema value may be null. var nullable: Bool? { get } diff --git a/Sources/FeatherOpenAPI/_Properties/RequiredProperty.swift b/Sources/FeatherOpenAPI/_Properties/RequiredProperty.swift index 653dec5..30ce25e 100644 --- a/Sources/FeatherOpenAPI/_Properties/RequiredProperty.swift +++ b/Sources/FeatherOpenAPI/_Properties/RequiredProperty.swift @@ -5,7 +5,6 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -/// Provides a `required` flag with a default value. public protocol RequiredProperty { /// Indicates whether the item is required. var required: Bool { get } diff --git a/Sources/FeatherOpenAPI/_Properties/TitleProperty.swift b/Sources/FeatherOpenAPI/_Properties/TitleProperty.swift index 436d2f5..2609ef6 100644 --- a/Sources/FeatherOpenAPI/_Properties/TitleProperty.swift +++ b/Sources/FeatherOpenAPI/_Properties/TitleProperty.swift @@ -5,7 +5,6 @@ // Created by Tibor Bödecs on 2026. 01. 23.. // -/// Provides a title property with a default value. public protocol TitleProperty { /// Short title. var title: String? { get } diff --git a/Tests/FeatherOpenAPITests/Example/Example.swift b/Tests/FeatherOpenAPITests/Example/Example.swift index b33805c..c7b24e6 100644 --- a/Tests/FeatherOpenAPITests/Example/Example.swift +++ b/Tests/FeatherOpenAPITests/Example/Example.swift @@ -4,11 +4,4 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - enum Example {} diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift index 3b5f230..97cf8e1 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - import FeatherOpenAPI import OpenAPIKit30 diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItem.swift b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItem.swift index 037a9f6..d25ddf5 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItem.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItem.swift @@ -4,11 +4,4 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - enum ExampleDuplicatedItem {} diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift index 853ded6..6e55e43 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - import FeatherOpenAPI import OpenAPIKit30 diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel+Schemas.swift index 43914d9..8c9c559 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel+Schemas.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - import FeatherOpenAPI extension ExampleDuplicatedItem.Model { diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel.swift b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel.swift index d3f9941..b4583a8 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - import FeatherOpenAPI extension ExampleDuplicatedItem { diff --git a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItem.swift b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItem.swift index 380b437..5fc05ab 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItem.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItem.swift @@ -4,11 +4,4 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - enum ExampleMissingParentItem {} diff --git a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift index d0f2d68..06ef392 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - import FeatherOpenAPI import OpenAPIKit30 diff --git a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel+Schemas.swift index c0a8423..d88e1bf 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel+Schemas.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - import FeatherOpenAPI extension ExampleMissingParentItem.Model { diff --git a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel.swift b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel.swift index c973673..50c11fe 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - import FeatherOpenAPI extension ExampleMissingParentItem { diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+Headers.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Headers.swift index 76f8caa..742a1a3 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+Headers.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Headers.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - import FeatherOpenAPI extension Example.Model { diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+Operations.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Operations.swift index cec899d..f61760f 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+Operations.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Operations.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - import FeatherOpenAPI import OpenAPIKit30 diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+Parameters.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Parameters.swift index ae21196..1a28a40 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+Parameters.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Parameters.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - import FeatherOpenAPI extension Example.Model { diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+PathItems.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+PathItems.swift index b616d70..d6a8acc 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+PathItems.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+PathItems.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - import FeatherOpenAPI extension Example.Model { diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+RequestBodies.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+RequestBodies.swift index fd3fa4d..982fce8 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+RequestBodies.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+RequestBodies.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - import FeatherOpenAPI extension Example.Model { diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+Responses.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Responses.swift index 91d03be..e32b5a0 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+Responses.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Responses.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - import FeatherOpenAPI import OpenAPIKit30 diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Schemas.swift index 5a0f176..c35bdd5 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Schemas.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - import FeatherOpenAPI import OpenAPIKit30 diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+SecuritySchemes.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+SecuritySchemes.swift index 256ceac..e29eec1 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+SecuritySchemes.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+SecuritySchemes.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - import FeatherOpenAPI import OpenAPIKit30 diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+Tags.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Tags.swift index 18a0d85..8b0d0c7 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+Tags.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Tags.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - import FeatherOpenAPI extension Example.Model { diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel.swift index 2e689c1..29feaee 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - import FeatherOpenAPI extension Example { diff --git a/Tests/FeatherOpenAPITests/ExampleTestSuite.swift b/Tests/FeatherOpenAPITests/ExampleTestSuite.swift index bedda2f..ac40cf3 100644 --- a/Tests/FeatherOpenAPITests/ExampleTestSuite.swift +++ b/Tests/FeatherOpenAPITests/ExampleTestSuite.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - import Foundation import OpenAPIKit import OpenAPIKit30 diff --git a/Tests/FeatherOpenAPITests/PathComponentTests.swift b/Tests/FeatherOpenAPITests/PathComponentTests.swift index e61f0d7..f796d43 100644 --- a/Tests/FeatherOpenAPITests/PathComponentTests.swift +++ b/Tests/FeatherOpenAPITests/PathComponentTests.swift @@ -4,17 +4,10 @@ // // Created by Tibor Bodecs on 2026. 01. 22.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 25/01/2024. -// - import Foundation import Testing -@testable import FeatherOpenAPIKit +@testable import FeatherOpenAPI extension Path { diff --git a/Tests/FeatherOpenAPITests/TodoTestSuite.swift b/Tests/FeatherOpenAPITests/TodoTestSuite.swift index 29bfb44..0240c1c 100644 --- a/Tests/FeatherOpenAPITests/TodoTestSuite.swift +++ b/Tests/FeatherOpenAPITests/TodoTestSuite.swift @@ -4,13 +4,6 @@ // // Created by Tibor Bodecs on 2026. 01. 25.. -// -// File.swift -// -// -// Created by Tibor Bodecs on 20/01/2024. -// - import Foundation import OpenAPIKit import OpenAPIKit30 From ef571d651a0023fea772dd3d1405900b8b896957 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sun, 25 Jan 2026 20:54:00 +0100 Subject: [PATCH 31/34] fix lang check --- .unacceptablelanguageignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .unacceptablelanguageignore diff --git a/.unacceptablelanguageignore b/.unacceptablelanguageignore new file mode 100644 index 0000000..4b37114 --- /dev/null +++ b/.unacceptablelanguageignore @@ -0,0 +1 @@ +Tests/FeatherOpenAPITests/Petstore/Petstore.swift From 312deee563fc7c329d099fbd9ffbdf9113bede16 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sun, 25 Jan 2026 21:47:16 +0100 Subject: [PATCH 32/34] fix my name --- Sources/FeatherOpenAPI/OrderedDictionary+Merge.swift | 2 +- Sources/FeatherOpenAPI/Path.swift | 2 +- Tests/FeatherOpenAPITests/Example/Example.swift | 2 +- Tests/FeatherOpenAPITests/Example/ExampleDocument.swift | 2 +- Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItem.swift | 2 +- .../Example/ExampleDuplicatedItemDocument.swift | 2 +- .../Example/ExampleDuplicatedItemModel+Schemas.swift | 2 +- .../Example/ExampleDuplicatedItemModel.swift | 2 +- .../FeatherOpenAPITests/Example/ExampleMissingParentItem.swift | 2 +- .../Example/ExampleMissingParentItemDocument.swift | 2 +- .../Example/ExampleMissingParentItemModel+Schemas.swift | 2 +- .../Example/ExampleMissingParentItemModel.swift | 2 +- Tests/FeatherOpenAPITests/Example/ExampleModel+Headers.swift | 2 +- Tests/FeatherOpenAPITests/Example/ExampleModel+Operations.swift | 2 +- Tests/FeatherOpenAPITests/Example/ExampleModel+Parameters.swift | 2 +- Tests/FeatherOpenAPITests/Example/ExampleModel+PathItems.swift | 2 +- .../Example/ExampleModel+RequestBodies.swift | 2 +- Tests/FeatherOpenAPITests/Example/ExampleModel+Responses.swift | 2 +- Tests/FeatherOpenAPITests/Example/ExampleModel+Schemas.swift | 2 +- .../Example/ExampleModel+SecuritySchemes.swift | 2 +- Tests/FeatherOpenAPITests/Example/ExampleModel+Tags.swift | 2 +- Tests/FeatherOpenAPITests/Example/ExampleModel.swift | 2 +- Tests/FeatherOpenAPITests/ExampleTestSuite.swift | 2 +- Tests/FeatherOpenAPITests/PathComponentTests.swift | 2 +- Tests/FeatherOpenAPITests/TodoTestSuite.swift | 2 +- 25 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Sources/FeatherOpenAPI/OrderedDictionary+Merge.swift b/Sources/FeatherOpenAPI/OrderedDictionary+Merge.swift index 62e796f..367141e 100644 --- a/Sources/FeatherOpenAPI/OrderedDictionary+Merge.swift +++ b/Sources/FeatherOpenAPI/OrderedDictionary+Merge.swift @@ -2,7 +2,7 @@ // OrderedDictionary+Merge.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import OpenAPIKit30 diff --git a/Sources/FeatherOpenAPI/Path.swift b/Sources/FeatherOpenAPI/Path.swift index 76c6f3d..767e229 100644 --- a/Sources/FeatherOpenAPI/Path.swift +++ b/Sources/FeatherOpenAPI/Path.swift @@ -2,7 +2,7 @@ // Path.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. /// A lightweight OpenAPI path wrapper with composition helpers. public struct Path: ExpressibleByStringLiteral { diff --git a/Tests/FeatherOpenAPITests/Example/Example.swift b/Tests/FeatherOpenAPITests/Example/Example.swift index c7b24e6..11e9bbc 100644 --- a/Tests/FeatherOpenAPITests/Example/Example.swift +++ b/Tests/FeatherOpenAPITests/Example/Example.swift @@ -2,6 +2,6 @@ // Example.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. enum Example {} diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift index 97cf8e1..5de9829 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDocument.swift @@ -2,7 +2,7 @@ // ExampleDocument.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import FeatherOpenAPI import OpenAPIKit30 diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItem.swift b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItem.swift index d25ddf5..786f0fd 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItem.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItem.swift @@ -2,6 +2,6 @@ // ExampleDuplicatedItem.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. enum ExampleDuplicatedItem {} diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift index 6e55e43..d470039 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemDocument.swift @@ -2,7 +2,7 @@ // ExampleDuplicatedItemDocument.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import FeatherOpenAPI import OpenAPIKit30 diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel+Schemas.swift index 8c9c559..dc750f8 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel+Schemas.swift @@ -2,7 +2,7 @@ // ExampleDuplicatedItemModel+Schemas.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import FeatherOpenAPI diff --git a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel.swift b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel.swift index b4583a8..9d6c829 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleDuplicatedItemModel.swift @@ -2,7 +2,7 @@ // ExampleDuplicatedItemModel.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import FeatherOpenAPI diff --git a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItem.swift b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItem.swift index 5fc05ab..06cc657 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItem.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItem.swift @@ -2,6 +2,6 @@ // ExampleMissingParentItem.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. enum ExampleMissingParentItem {} diff --git a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift index 06ef392..d2218ce 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemDocument.swift @@ -2,7 +2,7 @@ // ExampleMissingParentItemDocument.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import FeatherOpenAPI import OpenAPIKit30 diff --git a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel+Schemas.swift index d88e1bf..8eb3fcd 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel+Schemas.swift @@ -2,7 +2,7 @@ // ExampleMissingParentItemModel+Schemas.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import FeatherOpenAPI diff --git a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel.swift b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel.swift index 50c11fe..ef57733 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleMissingParentItemModel.swift @@ -2,7 +2,7 @@ // ExampleMissingParentItemModel.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import FeatherOpenAPI diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+Headers.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Headers.swift index 742a1a3..e819bbe 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+Headers.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Headers.swift @@ -2,7 +2,7 @@ // ExampleModel+Headers.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import FeatherOpenAPI diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+Operations.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Operations.swift index f61760f..6e5f17a 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+Operations.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Operations.swift @@ -2,7 +2,7 @@ // ExampleModel+Operations.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import FeatherOpenAPI import OpenAPIKit30 diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+Parameters.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Parameters.swift index 1a28a40..0424378 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+Parameters.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Parameters.swift @@ -2,7 +2,7 @@ // ExampleModel+Parameters.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import FeatherOpenAPI diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+PathItems.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+PathItems.swift index d6a8acc..5f90a47 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+PathItems.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+PathItems.swift @@ -2,7 +2,7 @@ // ExampleModel+PathItems.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import FeatherOpenAPI diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+RequestBodies.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+RequestBodies.swift index 982fce8..4754b08 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+RequestBodies.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+RequestBodies.swift @@ -2,7 +2,7 @@ // ExampleModel+RequestBodies.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import FeatherOpenAPI diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+Responses.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Responses.swift index e32b5a0..d2130ed 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+Responses.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Responses.swift @@ -2,7 +2,7 @@ // ExampleModel+Responses.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import FeatherOpenAPI import OpenAPIKit30 diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+Schemas.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Schemas.swift index c35bdd5..e937fe8 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Schemas.swift @@ -2,7 +2,7 @@ // ExampleModel+Schemas.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import FeatherOpenAPI import OpenAPIKit30 diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+SecuritySchemes.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+SecuritySchemes.swift index e29eec1..610ebc0 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+SecuritySchemes.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+SecuritySchemes.swift @@ -2,7 +2,7 @@ // ExampleModel+SecuritySchemes.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import FeatherOpenAPI import OpenAPIKit30 diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel+Tags.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel+Tags.swift index 8b0d0c7..89b0478 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel+Tags.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel+Tags.swift @@ -2,7 +2,7 @@ // ExampleModel+Tags.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import FeatherOpenAPI diff --git a/Tests/FeatherOpenAPITests/Example/ExampleModel.swift b/Tests/FeatherOpenAPITests/Example/ExampleModel.swift index 29feaee..934d6ba 100644 --- a/Tests/FeatherOpenAPITests/Example/ExampleModel.swift +++ b/Tests/FeatherOpenAPITests/Example/ExampleModel.swift @@ -2,7 +2,7 @@ // ExampleModel.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import FeatherOpenAPI diff --git a/Tests/FeatherOpenAPITests/ExampleTestSuite.swift b/Tests/FeatherOpenAPITests/ExampleTestSuite.swift index ac40cf3..690c5c3 100644 --- a/Tests/FeatherOpenAPITests/ExampleTestSuite.swift +++ b/Tests/FeatherOpenAPITests/ExampleTestSuite.swift @@ -2,7 +2,7 @@ // ExampleTestSuite.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import Foundation import OpenAPIKit diff --git a/Tests/FeatherOpenAPITests/PathComponentTests.swift b/Tests/FeatherOpenAPITests/PathComponentTests.swift index f796d43..3791761 100644 --- a/Tests/FeatherOpenAPITests/PathComponentTests.swift +++ b/Tests/FeatherOpenAPITests/PathComponentTests.swift @@ -2,7 +2,7 @@ // PathComponentTests.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 22.. +// Created by Tibor Bödecs on 2026. 01. 22.. import Foundation import Testing diff --git a/Tests/FeatherOpenAPITests/TodoTestSuite.swift b/Tests/FeatherOpenAPITests/TodoTestSuite.swift index 0240c1c..0d6c63f 100644 --- a/Tests/FeatherOpenAPITests/TodoTestSuite.swift +++ b/Tests/FeatherOpenAPITests/TodoTestSuite.swift @@ -2,7 +2,7 @@ // TodoTestSuite.swift // feather-openapi // -// Created by Tibor Bodecs on 2026. 01. 25.. +// Created by Tibor Bödecs on 2026. 01. 25.. import Foundation import OpenAPIKit From c0d258f6c310911bffb00e1853077acdcb166676 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Mon, 26 Jan 2026 09:56:55 +0100 Subject: [PATCH 33/34] petstore example --- .unacceptablelanguageignore | 2 + Petstore/openapi@3.0.yaml | 830 ++++++++++++++++++ Petstore/openapi@3.1.yaml | 291 ++++++ .../Components/ComponentsRepresentable.swift | 2 - .../ExampleTestSuite.swift | 1 - .../PathComponentTests.swift | 1 - .../ApiResponse/ApiResponse+Schemas.swift | 4 + .../Petstore/Category/Category+Schemas.swift | 3 + .../Petstore/Pet/Pet+Operations.swift | 208 ++++- .../Petstore/Pet/Pet+Parameters.swift | 81 +- .../Petstore/Pet/Pet+PathItems.swift | 18 +- .../Petstore/Pet/Pet+RequestBodies.swift | 54 ++ .../Petstore/Pet/Pet+Responses.swift | 34 +- .../Petstore/Pet/Pet+Schemas.swift | 49 +- .../Petstore/Pet/Pet+Tags.swift | 13 +- .../Petstore/Petstore+Security.swift | 62 ++ .../Petstore/Petstore.swift | 92 +- .../Petstore/Store/Store+Operations.swift | 98 +++ .../Petstore/Store/Store+Parameters.swift | 29 + .../Petstore/Store/Store+PathItems.swift | 24 + .../Petstore/Store/Store+RequestBodies.swift | 23 + .../Petstore/Store/Store+Responses.swift | 45 + .../Petstore/Store/Store+Schemas.swift | 56 +- .../Petstore/Store/Store+Tags.swift | 26 + .../Petstore/Tag/Tag+Schemas.swift | 3 + .../Petstore/User/User+Headers.swift | 51 ++ .../Petstore/User/User+Operations.swift | 149 ++++ .../Petstore/User/User+Parameters.swift | 55 ++ .../Petstore/User/User+PathItems.swift | 33 + .../Petstore/User/User+RequestBodies.swift | 56 ++ .../Petstore/User/User+Responses.swift | 46 + .../Petstore/User/User+Schemas.swift | 26 +- .../Petstore/User/User+Tags.swift | 16 + .../PetstoreTestSuite.swift | 12 +- Tests/FeatherOpenAPITests/TodoTestSuite.swift | 1 - 35 files changed, 2463 insertions(+), 31 deletions(-) create mode 100644 Petstore/openapi@3.0.yaml create mode 100644 Petstore/openapi@3.1.yaml create mode 100644 Tests/FeatherOpenAPITests/Petstore/Pet/Pet+RequestBodies.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/Petstore+Security.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/Store/Store+Operations.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/Store/Store+Parameters.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/Store/Store+PathItems.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/Store/Store+RequestBodies.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/Store/Store+Responses.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/Store/Store+Tags.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/User/User+Headers.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/User/User+Operations.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/User/User+Parameters.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/User/User+PathItems.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/User/User+RequestBodies.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/User/User+Responses.swift create mode 100644 Tests/FeatherOpenAPITests/Petstore/User/User+Tags.swift diff --git a/.unacceptablelanguageignore b/.unacceptablelanguageignore index 4b37114..48821ec 100644 --- a/.unacceptablelanguageignore +++ b/.unacceptablelanguageignore @@ -1 +1,3 @@ Tests/FeatherOpenAPITests/Petstore/Petstore.swift +Petstore/openapi@3.0.yaml +Petstore/openapi@3.1.yaml diff --git a/Petstore/openapi@3.0.yaml b/Petstore/openapi@3.0.yaml new file mode 100644 index 0000000..fc2fcd0 --- /dev/null +++ b/Petstore/openapi@3.0.yaml @@ -0,0 +1,830 @@ +openapi: 3.0.4 +info: + title: Swagger Petstore - OpenAPI 3.0 + description: |- + This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about + Swagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we've switched to the design first approach! + You can now help us improve the API whether it's by making changes to the definition itself or to the code. + That way, with time, we can improve the API in general, and expose some of the new features in OAS3. + + Some useful links: + - [The Pet Store repository](https://github.com/swagger-api/swagger-petstore) + - [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml) + termsOfService: https://swagger.io/terms/ + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + version: 1.0.27 +externalDocs: + description: Find out more about Swagger + url: https://swagger.io +servers: +- url: /api/v3 +tags: +- name: pet + description: Everything about your Pets + externalDocs: + description: Find out more + url: https://swagger.io +- name: store + description: Access to Petstore orders + externalDocs: + description: Find out more about our store + url: https://swagger.io +- name: user + description: Operations about user +paths: + /pet: + put: + tags: + - pet + summary: Update an existing pet. + description: Update an existing pet by Id. + operationId: updatePet + requestBody: + description: Update an existent pet in the store + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Pet' + required: true + responses: + "200": + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + "400": + description: Invalid ID supplied + "404": + description: Pet not found + "422": + description: Validation exception + default: + description: Unexpected error + security: + - petstore_auth: + - write:pets + - read:pets + post: + tags: + - pet + summary: Add a new pet to the store. + description: Add a new pet to the store. + operationId: addPet + requestBody: + description: Create a new pet in the store + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Pet' + required: true + responses: + "200": + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + "400": + description: Invalid input + "422": + description: Validation exception + default: + description: Unexpected error + security: + - petstore_auth: + - write:pets + - read:pets + /pet/findByStatus: + get: + tags: + - pet + summary: Finds Pets by status. + description: Multiple status values can be provided with comma separated strings. + operationId: findPetsByStatus + parameters: + - name: status + in: query + description: Status values that need to be considered for filter + required: true + explode: true + schema: + type: string + default: available + enum: + - available + - pending + - sold + responses: + "200": + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + "400": + description: Invalid status value + default: + description: Unexpected error + security: + - petstore_auth: + - write:pets + - read:pets + /pet/findByTags: + get: + tags: + - pet + summary: Finds Pets by tags. + description: "Multiple tags can be provided with comma separated strings. Use\ + \ tag1, tag2, tag3 for testing." + operationId: findPetsByTags + parameters: + - name: tags + in: query + description: Tags to filter by + required: true + explode: true + schema: + type: array + items: + type: string + responses: + "200": + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + "400": + description: Invalid tag value + default: + description: Unexpected error + security: + - petstore_auth: + - write:pets + - read:pets + /pet/{petId}: + get: + tags: + - pet + summary: Find pet by ID. + description: Returns a single pet. + operationId: getPetById + parameters: + - name: petId + in: path + description: ID of pet to return + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + "400": + description: Invalid ID supplied + "404": + description: Pet not found + default: + description: Unexpected error + security: + - api_key: [] + - petstore_auth: + - write:pets + - read:pets + post: + tags: + - pet + summary: Updates a pet in the store with form data. + description: Updates a pet resource based on the form data. + operationId: updatePetWithForm + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + schema: + type: integer + format: int64 + - name: name + in: query + description: Name of pet that needs to be updated + schema: + type: string + - name: status + in: query + description: Status of pet that needs to be updated + schema: + type: string + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + "400": + description: Invalid input + default: + description: Unexpected error + security: + - petstore_auth: + - write:pets + - read:pets + delete: + tags: + - pet + summary: Deletes a pet. + description: Delete a pet. + operationId: deletePet + parameters: + - name: api_key + in: header + description: "" + required: false + schema: + type: string + - name: petId + in: path + description: Pet id to delete + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: Pet deleted + "400": + description: Invalid pet value + default: + description: Unexpected error + security: + - petstore_auth: + - write:pets + - read:pets + /pet/{petId}/uploadImage: + post: + tags: + - pet + summary: Uploads an image. + description: Upload image of the pet. + operationId: uploadFile + parameters: + - name: petId + in: path + description: ID of pet to update + required: true + schema: + type: integer + format: int64 + - name: additionalMetadata + in: query + description: Additional Metadata + required: false + schema: + type: string + requestBody: + content: + application/octet-stream: + schema: + type: string + format: binary + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + "400": + description: No file uploaded + "404": + description: Pet not found + default: + description: Unexpected error + security: + - petstore_auth: + - write:pets + - read:pets + /store/inventory: + get: + tags: + - store + summary: Returns pet inventories by status. + description: Returns a map of status codes to quantities. + operationId: getInventory + responses: + "200": + description: successful operation + content: + application/json: + schema: + type: object + additionalProperties: + type: integer + format: int32 + default: + description: Unexpected error + security: + - api_key: [] + /store/order: + post: + tags: + - store + summary: Place an order for a pet. + description: Place a new order in the store. + operationId: placeOrder + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Order' + application/xml: + schema: + $ref: '#/components/schemas/Order' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Order' + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Order' + "400": + description: Invalid input + "422": + description: Validation exception + default: + description: Unexpected error + /store/order/{orderId}: + get: + tags: + - store + summary: Find purchase order by ID. + description: For valid response try integer IDs with value <= 5 or > 10. Other + values will generate exceptions. + operationId: getOrderById + parameters: + - name: orderId + in: path + description: ID of order that needs to be fetched + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Order' + application/xml: + schema: + $ref: '#/components/schemas/Order' + "400": + description: Invalid ID supplied + "404": + description: Order not found + default: + description: Unexpected error + delete: + tags: + - store + summary: Delete purchase order by identifier. + description: For valid response try integer IDs with value < 1000. Anything + above 1000 or non-integers will generate API errors. + operationId: deleteOrder + parameters: + - name: orderId + in: path + description: ID of the order that needs to be deleted + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: order deleted + "400": + description: Invalid ID supplied + "404": + description: Order not found + default: + description: Unexpected error + /user: + post: + tags: + - user + summary: Create user. + description: This can only be done by the logged in user. + operationId: createUser + requestBody: + description: Created user object + content: + application/json: + schema: + $ref: '#/components/schemas/User' + application/xml: + schema: + $ref: '#/components/schemas/User' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/User' + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/User' + application/xml: + schema: + $ref: '#/components/schemas/User' + default: + description: Unexpected error + /user/createWithList: + post: + tags: + - user + summary: Creates list of users with given input array. + description: Creates list of users with given input array. + operationId: createUsersWithListInput + requestBody: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + responses: + "200": + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/User' + application/xml: + schema: + $ref: '#/components/schemas/User' + default: + description: Unexpected error + /user/login: + get: + tags: + - user + summary: Logs user into the system. + description: Log into the system. + operationId: loginUser + parameters: + - name: username + in: query + description: The user name for login + required: false + schema: + type: string + - name: password + in: query + description: The password for login in clear text + required: false + schema: + type: string + responses: + "200": + description: successful operation + headers: + X-Rate-Limit: + description: calls per hour allowed by the user + schema: + type: integer + format: int32 + X-Expires-After: + description: date in UTC when token expires + schema: + type: string + format: date-time + content: + application/xml: + schema: + type: string + application/json: + schema: + type: string + "400": + description: Invalid username/password supplied + default: + description: Unexpected error + /user/logout: + get: + tags: + - user + summary: Logs out current logged in user session. + description: Log user out of the system. + operationId: logoutUser + parameters: [] + responses: + "200": + description: successful operation + default: + description: Unexpected error + /user/{username}: + get: + tags: + - user + summary: Get user by user name. + description: Get user detail based on username. + operationId: getUserByName + parameters: + - name: username + in: path + description: The name that needs to be fetched. Use user1 for testing + required: true + schema: + type: string + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/User' + application/xml: + schema: + $ref: '#/components/schemas/User' + "400": + description: Invalid username supplied + "404": + description: User not found + default: + description: Unexpected error + put: + tags: + - user + summary: Update user resource. + description: This can only be done by the logged in user. + operationId: updateUser + parameters: + - name: username + in: path + description: name that need to be deleted + required: true + schema: + type: string + requestBody: + description: Update an existent user in the store + content: + application/json: + schema: + $ref: '#/components/schemas/User' + application/xml: + schema: + $ref: '#/components/schemas/User' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/User' + responses: + "200": + description: successful operation + "400": + description: bad request + "404": + description: user not found + default: + description: Unexpected error + delete: + tags: + - user + summary: Delete user resource. + description: This can only be done by the logged in user. + operationId: deleteUser + parameters: + - name: username + in: path + description: The name that needs to be deleted + required: true + schema: + type: string + responses: + "200": + description: User deleted + "400": + description: Invalid username supplied + "404": + description: User not found + default: + description: Unexpected error +components: + schemas: + Order: + type: object + properties: + id: + type: integer + format: int64 + example: 10 + petId: + type: integer + format: int64 + example: 198772 + quantity: + type: integer + format: int32 + example: 7 + shipDate: + type: string + format: date-time + status: + type: string + description: Order Status + example: approved + enum: + - placed + - approved + - delivered + complete: + type: boolean + xml: + name: order + Category: + type: object + properties: + id: + type: integer + format: int64 + example: 1 + name: + type: string + example: Dogs + xml: + name: category + User: + type: object + properties: + id: + type: integer + format: int64 + example: 10 + username: + type: string + example: theUser + firstName: + type: string + example: John + lastName: + type: string + example: James + email: + type: string + example: john@email.com + password: + type: string + example: "12345" + phone: + type: string + example: "12345" + userStatus: + type: integer + description: User Status + format: int32 + example: 1 + xml: + name: user + Tag: + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: tag + Pet: + required: + - name + - photoUrls + type: object + properties: + id: + type: integer + format: int64 + example: 10 + name: + type: string + example: doggie + category: + $ref: '#/components/schemas/Category' + photoUrls: + type: array + xml: + wrapped: true + items: + type: string + xml: + name: photoUrl + tags: + type: array + xml: + wrapped: true + items: + $ref: '#/components/schemas/Tag' + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + xml: + name: pet + ApiResponse: + type: object + properties: + code: + type: integer + format: int32 + type: + type: string + message: + type: string + xml: + name: '##default' + requestBodies: + Pet: + description: Pet object that needs to be added to the store + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + UserArray: + description: List of user object + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + securitySchemes: + petstore_auth: + type: oauth2 + flows: + implicit: + authorizationUrl: https://petstore3.swagger.io/oauth/authorize + scopes: + write:pets: modify pets in your account + read:pets: read your pets + api_key: + type: apiKey + name: api_key + in: header diff --git a/Petstore/openapi@3.1.yaml b/Petstore/openapi@3.1.yaml new file mode 100644 index 0000000..97c23f6 --- /dev/null +++ b/Petstore/openapi@3.1.yaml @@ -0,0 +1,291 @@ +openapi: 3.1.0 +info: + title: Swagger Petstore - OpenAPI 3.1 + description: |- + This is a sample Pet Store Server based on the OpenAPI 3.1 specification. + You can find out more about + Swagger at [https://swagger.io](https://swagger.io). + termsOfService: https://swagger.io/terms/ + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + version: 1.0.10 + summary: Pet Store 3.1 + x-namespace: swagger +externalDocs: + description: Find out more about Swagger + url: https://swagger.io +servers: +- url: /api/v31 +tags: +- name: pet + description: Everything about your Pets + externalDocs: + description: Find out more + url: https://swagger.io +paths: + /pet: + put: + tags: + - pet + summary: Update an existing pet. + description: Update an existing pet by Id. + operationId: updatePet + requestBody: + description: Pet object that needs to be updated in the store + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in JSON Format + required: + - id + writeOnly: true + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in XML Format + required: + - id + writeOnly: true + required: true + responses: + "200": + description: Successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in XML Format + readOnly: true + application/json: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in JSON Format + readOnly: true + "400": + description: Invalid ID supplied + "404": + description: Pet not found + "405": + description: Validation exception + default: + description: Unexpected error + security: + - petstore_auth: + - write:pets + - read:pets + post: + tags: + - pet + summary: Add a new pet to the store. + description: Add a new pet to the store. + operationId: addPet + requestBody: + description: Create a new pet in the store + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in JSON Format + required: + - id + writeOnly: true + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in XML Format + required: + - id + writeOnly: true + required: true + responses: + "200": + description: Successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in XML Format + readOnly: true + application/json: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in JSON format + readOnly: true + "405": + description: Invalid input + default: + description: Unexpected error + security: + - petstore_auth: + - write:pets + - read:pets + /pet/{petId}: + get: + tags: + - pet + summary: Find pet by it's identifier. + description: Returns a pet when 0 < ID <= 10. ID > 10 or non-integers will + simulate API error conditions. + operationId: getPetById + parameters: + - name: petId + in: path + description: ID of pet that needs to be fetched + required: true + schema: + type: integer + format: int64 + description: param ID of pet that needs to be fetched + exclusiveMaximum: 10 + exclusiveMinimum: 1 + responses: + "200": + description: The pet + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in JSON format + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in XML format + "400": + description: Invalid ID supplied + "404": + description: Pet not found + default: + description: Unexpected error + security: + - petstore_auth: + - write:pets + - read:pets + - api_key: [] +components: + schemas: + Category: + $id: /api/v31/components/schemas/category + description: Category + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Category + Pet: + $schema: https://json-schema.org/draft/2020-12/schema + description: Pet + properties: + id: + type: integer + format: int64 + category: + $ref: '#/components/schemas/Category' + description: Pet Category + name: + type: string + examples: + - doggie + photoUrls: + type: array + items: + type: string + xml: + name: photoUrl + xml: + wrapped: true + tags: + type: array + items: + $ref: '#/components/schemas/Tag' + xml: + wrapped: true + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + availableInstances: + type: integer + format: int32 + examples: + - "7" + exclusiveMaximum: 10 + exclusiveMinimum: 1 + swagger-extension: true + petDetailsId: + type: integer + format: int64 + $ref: /api/v31/components/schemas/petdetails#pet_details_id + petDetails: + $ref: /api/v31/components/schemas/petdetails + required: + - name + - photoUrls + xml: + name: Pet + PetDetails: + $id: /api/v31/components/schemas/petdetails + $schema: https://json-schema.org/draft/2020-12/schema + $vocabulary: https://spec.openapis.org/oas/3.1/schema-base + properties: + id: + type: integer + format: int64 + $anchor: pet_details_id + examples: + - "10" + category: + $ref: /api/v31/components/schemas/category + description: PetDetails Category + tag: + $ref: /api/v31/components/schemas/tag + xml: + name: PetDetails + Tag: + $id: /api/v31/components/schemas/tag + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Tag + securitySchemes: + petstore_auth: + type: oauth2 + flows: + implicit: + authorizationUrl: https://petstore31.swagger.io/oauth/authorize + scopes: + write:pets: modify pets in your account + read:pets: read your pets + mutual_tls: + type: mutualTLS + api_key: + type: apiKey + name: api_key + in: header +webhooks: + newPet: + post: + requestBody: + description: Information about a new pet in the system + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + description: Webhook Pet + responses: + "200": + description: Return a 200 status to indicate that the data was received + successfully diff --git a/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift b/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift index 9a60c76..5b7c250 100644 --- a/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift +++ b/Sources/FeatherOpenAPI/Components/ComponentsRepresentable.swift @@ -158,8 +158,6 @@ extension ComponentsRepresentable { -> OpenAPI.ComponentDictionary { var result: OpenAPI.ComponentDictionary = [:] - - print(securityRequirements) for requirement in securityRequirements { let scheme = requirement.security result[.init(stringLiteral: scheme.openAPIIdentifier)] = diff --git a/Tests/FeatherOpenAPITests/ExampleTestSuite.swift b/Tests/FeatherOpenAPITests/ExampleTestSuite.swift index 690c5c3..8ca5449 100644 --- a/Tests/FeatherOpenAPITests/ExampleTestSuite.swift +++ b/Tests/FeatherOpenAPITests/ExampleTestSuite.swift @@ -4,7 +4,6 @@ // // Created by Tibor Bödecs on 2026. 01. 25.. -import Foundation import OpenAPIKit import OpenAPIKit30 import OpenAPIKitCompat diff --git a/Tests/FeatherOpenAPITests/PathComponentTests.swift b/Tests/FeatherOpenAPITests/PathComponentTests.swift index 3791761..cb96c28 100644 --- a/Tests/FeatherOpenAPITests/PathComponentTests.swift +++ b/Tests/FeatherOpenAPITests/PathComponentTests.swift @@ -4,7 +4,6 @@ // // Created by Tibor Bödecs on 2026. 01. 22.. -import Foundation import Testing @testable import FeatherOpenAPI diff --git a/Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse+Schemas.swift b/Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse+Schemas.swift index 6e85b4c..96448cc 100644 --- a/Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Petstore/ApiResponse/ApiResponse+Schemas.swift @@ -11,15 +11,19 @@ import OpenAPIKit30 extension Petstore.ApiResponse { struct CodeSchema: Int32SchemaRepresentable { + var required: Bool { false } } struct ResponseTypeSchema: StringSchemaRepresentable { + var required: Bool { false } } struct MessageSchema: StringSchemaRepresentable { + var required: Bool { false } } struct ApiResponseSchema: ObjectSchemaRepresentable { + var openAPIIdentifier: String { "ApiResponse" } var propertyMap: SchemaMap { [ "code": CodeSchema(), diff --git a/Tests/FeatherOpenAPITests/Petstore/Category/Category+Schemas.swift b/Tests/FeatherOpenAPITests/Petstore/Category/Category+Schemas.swift index 7b9f500..9ced790 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Category/Category+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Category/Category+Schemas.swift @@ -12,13 +12,16 @@ extension Petstore.Category { struct IdSchema: Int64SchemaRepresentable { var example: Int64? { 1 } + var required: Bool { false } } struct NameSchema: StringSchemaRepresentable { var example: String? { "Dogs" } + var required: Bool { false } } struct CategorySchema: ObjectSchemaRepresentable { + var openAPIIdentifier: String { "Category" } var propertyMap: SchemaMap { [ "id": IdSchema(), diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Operations.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Operations.swift index ddd7fd5..d5bcc17 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Operations.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Operations.swift @@ -10,19 +10,221 @@ import OpenAPIKit30 extension Petstore.Pet { - struct GetOperation: OperationRepresentable { + struct UpdateOperation: OperationRepresentable { var tags: [TagRepresentable] { [PetTag()] } var summary: String? { "Update an existing pet." } var description: String? { "Update an existing pet by Id." } var operationId: String? { "updatePet" } + var requestBody: RequestBodyRepresentable? { + UpdateRequestBody() + } + var responseMap: ResponseMap { + [ + 200: PetResponse(description: "Successful operation"), + 400: EmptyResponse(description: "Invalid ID supplied"), + 404: EmptyResponse(description: "Pet not found"), + 422: EmptyResponse(description: "Validation exception"), + .default: + EmptyResponse(description: "Unexpected error"), + ] + } + var security: [any SecurityRequirementRepresentable]? { + [ + PetstoreAuthSecurityRequirement() + ] + } + } + + struct AddOperation: OperationRepresentable { + var tags: [TagRepresentable] { [PetTag()] } + var summary: String? { "Add a new pet to the store." } + var description: String? { "Add a new pet to the store." } + var operationId: String? { "addPet" } + var requestBody: RequestBodyRepresentable? { + AddRequestBody() + } + var responseMap: ResponseMap { + [ + 200: PetResponse(description: "Successful operation"), + 400: EmptyResponse(description: "Invalid input"), + 422: EmptyResponse(description: "Validation exception"), + .default: + EmptyResponse(description: "Unexpected error"), + ] + } + var security: [any SecurityRequirementRepresentable]? { + [ + PetstoreAuthSecurityRequirement() + ] + } + } + + struct FindByStatusOperation: OperationRepresentable { + var tags: [TagRepresentable] { [PetTag()] } + var summary: String? { "Finds Pets by status." } + var description: String? { + "Multiple status values can be provided with comma separated strings." + } + var operationId: String? { "findPetsByStatus" } + var parameters: [ParameterRepresentable] { + [ + StatusQueryParameter() + ] + } + var responseMap: ResponseMap { + [ + 200: PetListResponse(description: "successful operation"), + 400: EmptyResponse(description: "Invalid status value"), + .default: + EmptyResponse(description: "Unexpected error"), + ] + } + var security: [any SecurityRequirementRepresentable]? { + [ + PetstoreAuthSecurityRequirement() + ] + } + } + + struct FindByTagsOperation: OperationRepresentable { + var tags: [TagRepresentable] { [PetTag()] } + var summary: String? { "Finds Pets by tags." } + var description: String? { + "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing." + } + var operationId: String? { "findPetsByTags" } + var parameters: [ParameterRepresentable] { + [ + TagsQueryParameter() + ] + } + var responseMap: ResponseMap { + [ + 200: PetListResponse(description: "successful operation"), + 400: EmptyResponse(description: "Invalid tag value"), + .default: + EmptyResponse(description: "Unexpected error"), + ] + } + var security: [any SecurityRequirementRepresentable]? { + [ + PetstoreAuthSecurityRequirement() + ] + } + } + + struct GetByIdOperation: OperationRepresentable { + var tags: [TagRepresentable] { [PetTag()] } + var summary: String? { "Find pet by ID." } + var description: String? { "Returns a single pet." } + var operationId: String? { "getPetById" } + var parameters: [ParameterRepresentable] { + [ + IdParameter() + ] + } + var responseMap: ResponseMap { + [ + 200: PetResponse(description: "successful operation"), + 400: EmptyResponse(description: "Invalid ID supplied"), + 404: EmptyResponse(description: "Pet not found"), + .default: + EmptyResponse(description: "Unexpected error"), + ] + } + var security: [any SecurityRequirementRepresentable]? { + [ + ApiKeySecurityRequirement(), + PetstoreAuthSecurityRequirement(), + ] + } + } + + struct UpdateWithFormOperation: OperationRepresentable { + var tags: [TagRepresentable] { [PetTag()] } + var summary: String? { + "Updates a pet in the store with form data." + } + var description: String? { + "Updates a pet resource based on the form data." + } + var operationId: String? { "updatePetWithForm" } + var parameters: [ParameterRepresentable] { + [ + UpdateIdParameter(), + NameQueryParameter(), + StatusUpdateQueryParameter(), + ] + } + var responseMap: ResponseMap { + [ + 200: PetResponse(description: "successful operation"), + 400: EmptyResponse(description: "Invalid input"), + .default: + EmptyResponse(description: "Unexpected error"), + ] + } + var security: [any SecurityRequirementRepresentable]? { + [ + PetstoreAuthSecurityRequirement() + ] + } + } + + struct DeleteOperation: OperationRepresentable { + var tags: [TagRepresentable] { [PetTag()] } + var summary: String? { "Deletes a pet." } + var description: String? { "Delete a pet." } + var operationId: String? { "deletePet" } var parameters: [ParameterRepresentable] { [ - IdParameter().reference() + ApiKeyHeaderParameter(), + DeleteIdParameter(), ] } var responseMap: ResponseMap { [ - 200: DetailResponse().reference() + 200: EmptyResponse(description: "Pet deleted"), + 400: EmptyResponse(description: "Invalid pet value"), + .default: + EmptyResponse(description: "Unexpected error"), + ] + } + var security: [any SecurityRequirementRepresentable]? { + [ + PetstoreAuthSecurityRequirement() + ] + } + } + + struct UploadImageOperation: OperationRepresentable { + var tags: [TagRepresentable] { [PetTag()] } + var summary: String? { "Uploads an image." } + var description: String? { "Upload image of the pet." } + var operationId: String? { "uploadFile" } + var parameters: [ParameterRepresentable] { + [ + UploadIdParameter(), + AdditionalMetadataQueryParameter(), + ] + } + var requestBody: RequestBodyRepresentable? { + UploadImageRequestBody() + } + var responseMap: ResponseMap { + [ + 200: ApiResponseJSONResponse( + description: "successful operation" + ), + 400: EmptyResponse(description: "No file uploaded"), + 404: EmptyResponse(description: "Pet not found"), + .default: + EmptyResponse(description: "Unexpected error"), + ] + } + var security: [any SecurityRequirementRepresentable]? { + [ + PetstoreAuthSecurityRequirement() ] } } diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Parameters.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Parameters.swift index 37075cf..1e26068 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Parameters.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Parameters.swift @@ -12,9 +12,88 @@ extension Petstore.Pet { struct IdParameter: PathParameterRepresentable { var name: String { "petId" } var description: String? { "ID of pet to return" } + var schema: any OpenAPISchemaRepresentable { + IdSchema() + } + } + + struct UpdateIdParameter: PathParameterRepresentable { + var name: String { "petId" } + var description: String? { "ID of pet that needs to be updated" } + var schema: any OpenAPISchemaRepresentable { + IdSchema() + } + } + + struct DeleteIdParameter: PathParameterRepresentable { + var name: String { "petId" } + var description: String? { "Pet id to delete" } + var schema: any OpenAPISchemaRepresentable { + IdSchema() + } + } + + struct UploadIdParameter: PathParameterRepresentable { + var name: String { "petId" } + var description: String? { "ID of pet to update" } + var schema: any OpenAPISchemaRepresentable { + IdSchema() + } + } + + struct StatusQueryParameter: QueryParameterRepresentable { + var name: String { "status" } + var description: String? { + "Status values that need to be considered for filter" + } var required: Bool { true } var schema: any OpenAPISchemaRepresentable { - IdSchema().reference() + StatusQuerySchema() + } + } + + struct TagsQueryParameter: QueryParameterRepresentable { + var name: String { "tags" } + var description: String? { "Tags to filter by" } + var required: Bool { true } + var schema: any OpenAPISchemaRepresentable { + TagsQuerySchema() + } + } + + struct NameQueryParameter: QueryParameterRepresentable { + var name: String { "name" } + var description: String? { "Name of pet that needs to be updated" } + var required: Bool { false } + var schema: any OpenAPISchemaRepresentable { + UpdateNameSchema() + } + } + + struct StatusUpdateQueryParameter: QueryParameterRepresentable { + var name: String { "status" } + var description: String? { "Status of pet that needs to be updated" } + var required: Bool { false } + var schema: any OpenAPISchemaRepresentable { + UpdateStatusSchema() + } + } + + struct ApiKeyHeaderParameter: HeaderParameterRepresentable { + var name: String { "api_key" } + var description: String? { "" } + var required: Bool { false } + var schema: any OpenAPISchemaRepresentable { + ApiKeySchema() + } + } + + struct AdditionalMetadataQueryParameter: QueryParameterRepresentable { + var name: String { "additionalMetadata" } + var description: String? { "Additional Metadata" } + var required: Bool { false } + var schema: any OpenAPISchemaRepresentable { + AdditionalMetadataSchema() } } } diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+PathItems.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+PathItems.swift index 54ac695..07dcc69 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+PathItems.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+PathItems.swift @@ -10,9 +10,25 @@ import FeatherOpenAPI extension Petstore.Pet { struct MainPathItem: PathItemRepresentable { + var put: OperationRepresentable? { UpdateOperation() } + var post: OperationRepresentable? { AddOperation() } + } + + struct FindByStatusPathItem: PathItemRepresentable { + var get: OperationRepresentable? { FindByStatusOperation() } + } + + struct FindByTagsPathItem: PathItemRepresentable { + var get: OperationRepresentable? { FindByTagsOperation() } } struct IdentifiedPathItem: PathItemRepresentable { - var get: OperationRepresentable? { GetOperation() } + var get: OperationRepresentable? { GetByIdOperation() } + var post: OperationRepresentable? { UpdateWithFormOperation() } + var delete: OperationRepresentable? { DeleteOperation() } + } + + struct UploadImagePathItem: PathItemRepresentable { + var post: OperationRepresentable? { UploadImageOperation() } } } diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+RequestBodies.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+RequestBodies.swift new file mode 100644 index 0000000..9bf8732 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+RequestBodies.swift @@ -0,0 +1,54 @@ +// +// Pet+RequestBodies.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI +import OpenAPIKit30 + +extension Petstore.Pet { + + struct UpdateRequestBody: RequestBodyRepresentable { + var description: String? { "Update an existent pet in the store" } + var required: Bool { true } + var contentMap: ContentMap { + [ + .json: Content(PetSchema().reference()), + .xml: Content(PetSchema().reference()), + .form: Content(PetSchema().reference()), + ] + } + } + + struct AddRequestBody: RequestBodyRepresentable { + var description: String? { "Create a new pet in the store" } + var required: Bool { true } + var contentMap: ContentMap { + [ + .json: Content(PetSchema().reference()), + .xml: Content(PetSchema().reference()), + .form: Content(PetSchema().reference()), + ] + } + } + + struct UploadImageRequestBody: BinaryRequestBodyRepresentable { + var required: Bool { false } + } + + struct PetComponentRequestBody: RequestBodyRepresentable { + var openAPIIdentifier: String { "Pet" } + var description: String? { + "Pet object that needs to be added to the store" + } + var required: Bool { false } + var contentMap: ContentMap { + [ + .json: Content(PetSchema().reference()), + .xml: Content(PetSchema().reference()), + ] + } + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Responses.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Responses.swift index 88eac3a..85ea59f 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Responses.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Responses.swift @@ -10,13 +10,39 @@ import OpenAPIKit30 extension Petstore.Pet { - struct DetailResponse: ResponseRepresentable { - var description: String { "Successful operation" } + struct PetResponse: ResponseRepresentable { + let description: String var contentMap: ContentMap { [ - .json: Content(Schema()), - .xml: Content(Schema()), + .json: Content(PetSchema().reference()), + .xml: Content(PetSchema().reference()), ] } } + + struct PetListResponse: ResponseRepresentable { + let description: String + var contentMap: ContentMap { + [ + .json: Content(PetListSchema()), + .xml: Content(PetListSchema()), + ] + } + } + + struct ApiResponseJSONResponse: ResponseRepresentable { + let description: String + var contentMap: ContentMap { + [ + .json: Content( + Petstore.ApiResponse.ApiResponseSchema().reference() + ) + ] + } + } + + struct EmptyResponse: ResponseRepresentable { + let description: String + var contentMap: ContentMap { [:] } + } } diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Schemas.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Schemas.swift index 180b8be..027f202 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Schemas.swift @@ -12,6 +12,7 @@ extension Petstore.Pet { struct IdSchema: Int64SchemaRepresentable { var example: Int64? { 10 } + var required: Bool { false } } struct NameSchema: StringSchemaRepresentable { @@ -27,7 +28,7 @@ extension Petstore.Pet { struct TagsSchema: ArraySchemaRepresentable { var required: Bool { false } - var items: SchemaRepresentable? { Petstore.Tag.TagSchema() } + var items: SchemaRepresentable? { Petstore.Tag.TagSchema().reference() } } struct StatusSchema: StringSchemaRepresentable { @@ -39,19 +40,59 @@ extension Petstore.Pet { "sold", ] } + var required: Bool { false } } - struct Schema: ObjectSchemaRepresentable { + struct PetSchema: ObjectSchemaRepresentable { + var openAPIIdentifier: String { "Pet" } var propertyMap: SchemaMap { [ - "id": IdSchema().reference(required: false), + "id": IdSchema(), "name": NameSchema(), "category": Petstore.Category.CategorySchema() .reference(required: false), "photoUrls": PhotoUrlsSchema(), "tags": TagsSchema(), - "status": StatusSchema().reference(required: false), + "status": StatusSchema(), ] } } + + struct PetListSchema: ArraySchemaRepresentable { + var items: SchemaRepresentable? { PetSchema().reference() } + } + + struct StatusQuerySchema: StringSchemaRepresentable { + var defaultValue: String? { "available" } + var allowedValues: [String]? { + [ + "available", + "pending", + "sold", + ] + } + } + + struct TagsQueryItemSchema: StringSchemaRepresentable { + } + + struct TagsQuerySchema: ArraySchemaRepresentable { + var items: SchemaRepresentable? { TagsQueryItemSchema() } + } + + struct UpdateNameSchema: StringSchemaRepresentable { + var required: Bool { false } + } + + struct UpdateStatusSchema: StringSchemaRepresentable { + var required: Bool { false } + } + + struct AdditionalMetadataSchema: StringSchemaRepresentable { + var required: Bool { false } + } + + struct ApiKeySchema: StringSchemaRepresentable { + var required: Bool { false } + } } diff --git a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Tags.swift b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Tags.swift index 638027e..c6fa2eb 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Tags.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Pet/Pet+Tags.swift @@ -10,6 +10,17 @@ import FeatherOpenAPI extension Petstore.Pet { struct PetTag: TagRepresentable { - var name: String { "Pet" } + var name: String { "pet" } + var description: String? { "Everything about your Pets" } + var externalDocs: ExternalDocsRepresentable? { + PetTagExternalDocs() + } + } + + struct PetTagExternalDocs: ExternalDocsRepresentable { + var description: String? { "Find out more" } + var url: LocationRepresentable { + PetstoreLocation(location: "https://swagger.io") + } } } diff --git a/Tests/FeatherOpenAPITests/Petstore/Petstore+Security.swift b/Tests/FeatherOpenAPITests/Petstore/Petstore+Security.swift new file mode 100644 index 0000000..f709ad7 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/Petstore+Security.swift @@ -0,0 +1,62 @@ +// +// Petstore+Security.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +import FeatherOpenAPI +import OpenAPIKit30 + +struct PetstoreAuthSecurityScheme: SecuritySchemeRepresentable { + + var type: OpenAPI.SecurityScheme.SecurityType { + .oauth2( + flows: .init( + implicit: .init( + authorizationUrl: URL( + string: "https://petstore3.swagger.io/oauth/authorize" + )!, + refreshUrl: nil, + scopes: [ + "write:pets": "modify pets in your account", + "read:pets": "read your pets", + ] + ) + ) + ) + } +} + +struct ApiKeySecurityScheme: SecuritySchemeRepresentable { + var type: OpenAPI.SecurityScheme.SecurityType { + .apiKey( + name: "api_key", + location: .header + ) + } +} + +struct PetstoreAuthSecurityRequirement: SecurityRequirementRepresentable { + var security: any SecuritySchemeRepresentable { + PetstoreAuthSecurityScheme() + } + var requirements: [String] { + [ + "write:pets", + "read:pets", + ] + } +} + +struct ApiKeySecurityRequirement: SecurityRequirementRepresentable { + var security: any SecuritySchemeRepresentable { + ApiKeySecurityScheme() + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/Petstore.swift b/Tests/FeatherOpenAPITests/Petstore/Petstore.swift index 743f908..324ee94 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Petstore.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Petstore.swift @@ -53,21 +53,107 @@ struct PetstoreServer: ServerRepresentable { var url: LocationRepresentable { PetstoreLocation(location: "/api/v3") } } +struct PetstoreExternalDocs: ExternalDocsRepresentable { + var description: String? { "Find out more about Swagger" } + var url: LocationRepresentable { + PetstoreLocation(location: "https://swagger.io") + } +} + struct PetstorePathCollection: PathCollectionRepresentable { var pathMap: PathMap { [ - "pet": Petstore.Pet.MainPathItem(), - "pet/{petId}": Petstore.Pet.IdentifiedPathItem(), + "/pet": Petstore.Pet.MainPathItem(), + "/pet/findByStatus": Petstore.Pet.FindByStatusPathItem(), + "/pet/findByTags": Petstore.Pet.FindByTagsPathItem(), + "/pet/{petId}": Petstore.Pet.IdentifiedPathItem(), + "/pet/{petId}/uploadImage": Petstore.Pet.UploadImagePathItem(), + "/store/inventory": Petstore.Store.InventoryPathItem(), + "/store/order": Petstore.Store.OrderPathItem(), + "/store/order/{orderId}": Petstore.Store.OrderIdentifiedPathItem(), + "/user": Petstore.User.MainPathItem(), + "/user/createWithList": Petstore.User.CreateWithListPathItem(), + "/user/login": Petstore.User.LoginPathItem(), + "/user/logout": Petstore.User.LogoutPathItem(), + "/user/{username}": Petstore.User.IdentifiedPathItem(), ] } } +struct PetstoreComponents: ComponentsRepresentable { + let base: FeatherOpenAPI.Components + + var schemas: OrderedDictionary { + base.schemas + } + + var parameters: + OrderedDictionary + { + base.parameters + } + + var examples: OrderedDictionary { + base.examples + } + + var responses: OrderedDictionary { + base.responses + } + + var requestBodies: + OrderedDictionary< + RequestBodyID, OpenAPIRequestBodyRepresentable + > + { + var results = base.requestBodies + + let petRequestBody = Petstore.Pet.PetComponentRequestBody() + results[.init(petRequestBody.openAPIIdentifier)] = petRequestBody + + let userArrayRequestBody = + Petstore.User.UserArrayComponentRequestBody() + results[.init(userArrayRequestBody.openAPIIdentifier)] = + userArrayRequestBody + + return results + } + + var headers: OrderedDictionary { + base.headers + } + + var securityRequirements: [SecurityRequirementRepresentable] { + base.securityRequirements + } + + var links: OrderedDictionary { + base.links + } +} + struct PetstoreDocument: DocumentRepresentable { let collection = PetstorePathCollection() var info: OpenAPIInfoRepresentable { PetstoreInfo() } var servers: [OpenAPIServerRepresentable] { [PetstoreServer()] } + var externalDocs: ExternalDocsRepresentable? { PetstoreExternalDocs() } + var paths: PathMap { collection.pathMap } - var components: OpenAPIComponentsRepresentable { collection.components } + var components: OpenAPIComponentsRepresentable { + PetstoreComponents(base: collection.components) + } + + var referencedTags: [OpenAPITagRepresentable] { + [ + Petstore.Pet.PetTag(), + Petstore.Store.StoreTag(), + Petstore.User.UserTag(), + ] + } + + var referencedSecurityRequirements: [SecurityRequirementRepresentable] { + [] + } } diff --git a/Tests/FeatherOpenAPITests/Petstore/Store/Store+Operations.swift b/Tests/FeatherOpenAPITests/Petstore/Store/Store+Operations.swift new file mode 100644 index 0000000..e8fc46a --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/Store/Store+Operations.swift @@ -0,0 +1,98 @@ +// +// Store+Operations.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI +import OpenAPIKit30 + +extension Petstore.Store { + + struct InventoryOperation: OperationRepresentable { + var tags: [TagRepresentable] { [StoreTag()] } + var summary: String? { "Returns pet inventories by status." } + var description: String? { + "Returns a map of status codes to quantities." + } + var operationId: String? { "getInventory" } + var responseMap: ResponseMap { + [ + 200: InventoryResponse(description: "successful operation"), + .default: + EmptyResponse(description: "Unexpected error"), + ] + } + var security: [any SecurityRequirementRepresentable]? { + [ + ApiKeySecurityRequirement() + ] + } + } + + struct PlaceOrderOperation: OperationRepresentable { + var tags: [TagRepresentable] { [StoreTag()] } + var summary: String? { "Place an order for a pet." } + var description: String? { "Place a new order in the store." } + var operationId: String? { "placeOrder" } + var requestBody: RequestBodyRepresentable? { + PlaceOrderRequestBody() + } + var responseMap: ResponseMap { + [ + 200: OrderJSONResponse(description: "successful operation"), + 400: EmptyResponse(description: "Invalid input"), + 422: EmptyResponse(description: "Validation exception"), + .default: + EmptyResponse(description: "Unexpected error"), + ] + } + } + + struct GetOrderOperation: OperationRepresentable { + var tags: [TagRepresentable] { [StoreTag()] } + var summary: String? { "Find purchase order by ID." } + var description: String? { + "For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions." + } + var operationId: String? { "getOrderById" } + var parameters: [ParameterRepresentable] { + [ + OrderIdParameter() + ] + } + var responseMap: ResponseMap { + [ + 200: OrderResponse(description: "successful operation"), + 400: EmptyResponse(description: "Invalid ID supplied"), + 404: EmptyResponse(description: "Order not found"), + .default: + EmptyResponse(description: "Unexpected error"), + ] + } + } + + struct DeleteOrderOperation: OperationRepresentable { + var tags: [TagRepresentable] { [StoreTag()] } + var summary: String? { "Delete purchase order by identifier." } + var description: String? { + "For valid response try integer IDs with value < 1000. Anything above 1000 or non-integers will generate API errors." + } + var operationId: String? { "deleteOrder" } + var parameters: [ParameterRepresentable] { + [ + OrderIdDeleteParameter() + ] + } + var responseMap: ResponseMap { + [ + 200: EmptyResponse(description: "order deleted"), + 400: EmptyResponse(description: "Invalid ID supplied"), + 404: EmptyResponse(description: "Order not found"), + .default: + EmptyResponse(description: "Unexpected error"), + ] + } + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/Store/Store+Parameters.swift b/Tests/FeatherOpenAPITests/Petstore/Store/Store+Parameters.swift new file mode 100644 index 0000000..aad03d0 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/Store/Store+Parameters.swift @@ -0,0 +1,29 @@ +// +// Store+Parameters.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore.Store { + + struct OrderIdParameter: PathParameterRepresentable { + var name: String { "orderId" } + var description: String? { "ID of order that needs to be fetched" } + var schema: any OpenAPISchemaRepresentable { + OrderIdSchema() + } + } + + struct OrderIdDeleteParameter: PathParameterRepresentable { + var name: String { "orderId" } + var description: String? { + "ID of the order that needs to be deleted" + } + var schema: any OpenAPISchemaRepresentable { + OrderIdSchema() + } + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/Store/Store+PathItems.swift b/Tests/FeatherOpenAPITests/Petstore/Store/Store+PathItems.swift new file mode 100644 index 0000000..e5ca791 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/Store/Store+PathItems.swift @@ -0,0 +1,24 @@ +// +// Store+PathItems.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore.Store { + + struct InventoryPathItem: PathItemRepresentable { + var get: OperationRepresentable? { InventoryOperation() } + } + + struct OrderPathItem: PathItemRepresentable { + var post: OperationRepresentable? { PlaceOrderOperation() } + } + + struct OrderIdentifiedPathItem: PathItemRepresentable { + var get: OperationRepresentable? { GetOrderOperation() } + var delete: OperationRepresentable? { DeleteOrderOperation() } + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/Store/Store+RequestBodies.swift b/Tests/FeatherOpenAPITests/Petstore/Store/Store+RequestBodies.swift new file mode 100644 index 0000000..3daa84b --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/Store/Store+RequestBodies.swift @@ -0,0 +1,23 @@ +// +// Store+RequestBodies.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI +import OpenAPIKit30 + +extension Petstore.Store { + + struct PlaceOrderRequestBody: RequestBodyRepresentable { + var required: Bool { false } + var contentMap: ContentMap { + [ + .json: Content(OrderSchema().reference()), + .xml: Content(OrderSchema().reference()), + .form: Content(OrderSchema().reference()), + ] + } + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/Store/Store+Responses.swift b/Tests/FeatherOpenAPITests/Petstore/Store/Store+Responses.swift new file mode 100644 index 0000000..37bcf59 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/Store/Store+Responses.swift @@ -0,0 +1,45 @@ +// +// Store+Responses.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI +import OpenAPIKit30 + +extension Petstore.Store { + + struct OrderResponse: ResponseRepresentable { + let description: String + var contentMap: ContentMap { + [ + .json: Content(OrderSchema().reference()), + .xml: Content(OrderSchema().reference()), + ] + } + } + + struct OrderJSONResponse: ResponseRepresentable { + let description: String + var contentMap: ContentMap { + [ + .json: Content(OrderSchema().reference()) + ] + } + } + + struct InventoryResponse: ResponseRepresentable { + let description: String + var contentMap: ContentMap { + [ + .json: Content(InventorySchema()) + ] + } + } + + struct EmptyResponse: ResponseRepresentable { + let description: String + var contentMap: ContentMap { [:] } + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/Store/Store+Schemas.swift b/Tests/FeatherOpenAPITests/Petstore/Store/Store+Schemas.swift index 7fa3c25..3b5fb7f 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Store/Store+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Store/Store+Schemas.swift @@ -12,17 +12,40 @@ extension Petstore.Store { struct OrderIdSchema: Int64SchemaRepresentable { var example: Int64? { 10 } + var required: Bool { false } } struct OrderPetIdSchema: Int64SchemaRepresentable { var example: Int64? { 198772 } + var required: Bool { false } } struct OrderQuantitySchema: Int32SchemaRepresentable { var example: Int32? { 7 } + var required: Bool { false } } - struct OrderShipDateSchema: StringSchemaRepresentable { + struct OrderShipDateSchema: SchemaRepresentable { + var required: Bool { false } + func openAPISchema() -> JSONSchema { + .string( + format: .dateTime, + required: `required`, + nullable: nullable, + permissions: nil, + deprecated: deprecated, + title: title, + description: description, + discriminator: nil, + externalDocs: nil, + minLength: nil, + maxLength: nil, + pattern: nil, + allowedValues: nil, + defaultValue: nil, + example: nil + ) + } } struct OrderStatusSchema: StringSchemaRepresentable { @@ -35,12 +58,15 @@ extension Petstore.Store { ] } var example: String? { "approved" } + var required: Bool { false } } struct OrderCompleteSchema: BoolSchemaRepresentable { + var required: Bool { false } } struct OrderSchema: ObjectSchemaRepresentable { + var openAPIIdentifier: String { "Order" } var propertyMap: SchemaMap { [ "id": OrderIdSchema(), @@ -52,4 +78,32 @@ extension Petstore.Store { ] } } + + struct InventorySchema: SchemaRepresentable { + func openAPISchema() -> JSONSchema { + .object( + format: .generic, + required: `required`, + nullable: nullable, + permissions: nil, + deprecated: deprecated, + title: title, + description: description, + discriminator: nil, + externalDocs: nil, + minProperties: nil, + maxProperties: nil, + properties: [:], + additionalProperties: .schema( + InventoryQuantitySchema().openAPISchema() + ), + allowedValues: nil, + defaultValue: nil, + example: nil + ) + } + } + + struct InventoryQuantitySchema: Int32SchemaRepresentable { + } } diff --git a/Tests/FeatherOpenAPITests/Petstore/Store/Store+Tags.swift b/Tests/FeatherOpenAPITests/Petstore/Store/Store+Tags.swift new file mode 100644 index 0000000..15c7eb8 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/Store/Store+Tags.swift @@ -0,0 +1,26 @@ +// +// Store+Tags.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore.Store { + + struct StoreTag: TagRepresentable { + var name: String { "store" } + var description: String? { "Access to Petstore orders" } + var externalDocs: ExternalDocsRepresentable? { + StoreTagExternalDocs() + } + } + + struct StoreTagExternalDocs: ExternalDocsRepresentable { + var description: String? { "Find out more about our store" } + var url: LocationRepresentable { + PetstoreLocation(location: "https://swagger.io") + } + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/Tag/Tag+Schemas.swift b/Tests/FeatherOpenAPITests/Petstore/Tag/Tag+Schemas.swift index d253e2d..1b176ba 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Tag/Tag+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Tag/Tag+Schemas.swift @@ -11,12 +11,15 @@ import OpenAPIKit30 extension Petstore.Tag { struct IdSchema: Int64SchemaRepresentable { + var required: Bool { false } } struct NameSchema: StringSchemaRepresentable { + var required: Bool { false } } struct TagSchema: ObjectSchemaRepresentable { + var openAPIIdentifier: String { "Tag" } var propertyMap: SchemaMap { [ "id": IdSchema(), diff --git a/Tests/FeatherOpenAPITests/Petstore/User/User+Headers.swift b/Tests/FeatherOpenAPITests/Petstore/User/User+Headers.swift new file mode 100644 index 0000000..f8d4f9c --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/User/User+Headers.swift @@ -0,0 +1,51 @@ +// +// User+Headers.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI +import OpenAPIKit30 + +extension Petstore.User { + + struct RateLimitHeader: HeaderRepresentable { + var description: String? { "calls per hour allowed by the user" } + var schema: any OpenAPISchemaRepresentable { + RateLimitSchema() + } + } + + struct ExpiresAfterHeader: HeaderRepresentable { + var description: String? { "date in UTC when token expires" } + var schema: any OpenAPISchemaRepresentable { + ExpiresAfterSchema() + } + } + + struct RateLimitSchema: Int32SchemaRepresentable { + } + + struct ExpiresAfterSchema: SchemaRepresentable { + func openAPISchema() -> JSONSchema { + .string( + format: .dateTime, + required: `required`, + nullable: nullable, + permissions: nil, + deprecated: deprecated, + title: title, + description: description, + discriminator: nil, + externalDocs: nil, + minLength: nil, + maxLength: nil, + pattern: nil, + allowedValues: nil, + defaultValue: nil, + example: nil + ) + } + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/User/User+Operations.swift b/Tests/FeatherOpenAPITests/Petstore/User/User+Operations.swift new file mode 100644 index 0000000..397000f --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/User/User+Operations.swift @@ -0,0 +1,149 @@ +// +// User+Operations.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI +import OpenAPIKit30 + +extension Petstore.User { + + struct CreateOperation: OperationRepresentable { + var tags: [TagRepresentable] { [UserTag()] } + var summary: String? { "Create user." } + var description: String? { + "This can only be done by the logged in user." + } + var operationId: String? { "createUser" } + var requestBody: RequestBodyRepresentable? { CreateRequestBody() } + var responseMap: ResponseMap { + [ + 200: UserResponse(description: "successful operation"), + .default: + EmptyResponse(description: "Unexpected error"), + ] + } + } + + struct CreateWithListOperation: OperationRepresentable { + var tags: [TagRepresentable] { [UserTag()] } + var summary: String? { "Creates list of users with given input array." } + var description: String? { "Creates list of users with given input array." } + var operationId: String? { "createUsersWithListInput" } + var requestBody: RequestBodyRepresentable? { + CreateWithListRequestBody() + } + var responseMap: ResponseMap { + [ + 200: UserResponse(description: "Successful operation"), + .default: + EmptyResponse(description: "Unexpected error"), + ] + } + } + + struct LoginOperation: OperationRepresentable { + var tags: [TagRepresentable] { [UserTag()] } + var summary: String? { "Logs user into the system." } + var description: String? { "Log into the system." } + var operationId: String? { "loginUser" } + var parameters: [ParameterRepresentable] { + [ + LoginUsernameParameter(), + LoginPasswordParameter(), + ] + } + var responseMap: ResponseMap { + [ + 200: LoginResponse(), + 400: EmptyResponse(description: "Invalid username/password supplied"), + .default: + EmptyResponse(description: "Unexpected error"), + ] + } + } + + struct LogoutOperation: OperationRepresentable { + var tags: [TagRepresentable] { [UserTag()] } + var summary: String? { "Logs out current logged in user session." } + var description: String? { "Log user out of the system." } + var operationId: String? { "logoutUser" } + var responseMap: ResponseMap { + [ + 200: EmptyResponse(description: "successful operation"), + .default: + EmptyResponse(description: "Unexpected error"), + ] + } + } + + struct GetByNameOperation: OperationRepresentable { + var tags: [TagRepresentable] { [UserTag()] } + var summary: String? { "Get user by user name." } + var description: String? { "Get user detail based on username." } + var operationId: String? { "getUserByName" } + var parameters: [ParameterRepresentable] { + [ + UsernameParameter() + ] + } + var responseMap: ResponseMap { + [ + 200: UserResponse(description: "successful operation"), + 400: EmptyResponse(description: "Invalid username supplied"), + 404: EmptyResponse(description: "User not found"), + .default: + EmptyResponse(description: "Unexpected error"), + ] + } + } + + struct UpdateOperation: OperationRepresentable { + var tags: [TagRepresentable] { [UserTag()] } + var summary: String? { "Update user resource." } + var description: String? { + "This can only be done by the logged in user." + } + var operationId: String? { "updateUser" } + var parameters: [ParameterRepresentable] { + [ + UpdateUsernameParameter() + ] + } + var requestBody: RequestBodyRepresentable? { UpdateRequestBody() } + var responseMap: ResponseMap { + [ + 200: EmptyResponse(description: "successful operation"), + 400: EmptyResponse(description: "bad request"), + 404: EmptyResponse(description: "user not found"), + .default: + EmptyResponse(description: "Unexpected error"), + ] + } + } + + struct DeleteOperation: OperationRepresentable { + var tags: [TagRepresentable] { [UserTag()] } + var summary: String? { "Delete user resource." } + var description: String? { + "This can only be done by the logged in user." + } + var operationId: String? { "deleteUser" } + var parameters: [ParameterRepresentable] { + [ + DeleteUsernameParameter() + ] + } + var responseMap: ResponseMap { + [ + 200: EmptyResponse(description: "User deleted"), + 400: EmptyResponse(description: "Invalid username supplied"), + 404: EmptyResponse(description: "User not found"), + .default: + EmptyResponse(description: "Unexpected error"), + ] + } + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/User/User+Parameters.swift b/Tests/FeatherOpenAPITests/Petstore/User/User+Parameters.swift new file mode 100644 index 0000000..314e929 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/User/User+Parameters.swift @@ -0,0 +1,55 @@ +// +// User+Parameters.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore.User { + + struct LoginUsernameParameter: QueryParameterRepresentable { + var name: String { "username" } + var description: String? { "The user name for login" } + var required: Bool { false } + var schema: any OpenAPISchemaRepresentable { + LoginUsernameSchema() + } + } + + struct LoginPasswordParameter: QueryParameterRepresentable { + var name: String { "password" } + var description: String? { "The password for login in clear text" } + var required: Bool { false } + var schema: any OpenAPISchemaRepresentable { + LoginPasswordSchema() + } + } + + struct UsernameParameter: PathParameterRepresentable { + var name: String { "username" } + var description: String? { + "The name that needs to be fetched. Use user1 for testing" + } + var schema: any OpenAPISchemaRepresentable { + UsernameSchema() + } + } + + struct UpdateUsernameParameter: PathParameterRepresentable { + var name: String { "username" } + var description: String? { "name that need to be deleted" } + var schema: any OpenAPISchemaRepresentable { + UsernameSchema() + } + } + + struct DeleteUsernameParameter: PathParameterRepresentable { + var name: String { "username" } + var description: String? { "The name that needs to be deleted" } + var schema: any OpenAPISchemaRepresentable { + UsernameSchema() + } + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/User/User+PathItems.swift b/Tests/FeatherOpenAPITests/Petstore/User/User+PathItems.swift new file mode 100644 index 0000000..6a50c75 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/User/User+PathItems.swift @@ -0,0 +1,33 @@ +// +// User+PathItems.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore.User { + + struct MainPathItem: PathItemRepresentable { + var post: OperationRepresentable? { CreateOperation() } + } + + struct CreateWithListPathItem: PathItemRepresentable { + var post: OperationRepresentable? { CreateWithListOperation() } + } + + struct LoginPathItem: PathItemRepresentable { + var get: OperationRepresentable? { LoginOperation() } + } + + struct LogoutPathItem: PathItemRepresentable { + var get: OperationRepresentable? { LogoutOperation() } + } + + struct IdentifiedPathItem: PathItemRepresentable { + var get: OperationRepresentable? { GetByNameOperation() } + var put: OperationRepresentable? { UpdateOperation() } + var delete: OperationRepresentable? { DeleteOperation() } + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/User/User+RequestBodies.swift b/Tests/FeatherOpenAPITests/Petstore/User/User+RequestBodies.swift new file mode 100644 index 0000000..a2f7dd0 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/User/User+RequestBodies.swift @@ -0,0 +1,56 @@ +// +// User+RequestBodies.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI +import OpenAPIKit30 + +extension Petstore.User { + + struct CreateRequestBody: RequestBodyRepresentable { + var description: String? { "Created user object" } + var required: Bool { false } + var contentMap: ContentMap { + [ + .json: Content(UserSchema().reference()), + .xml: Content(UserSchema().reference()), + .form: Content(UserSchema().reference()), + ] + } + } + + struct UpdateRequestBody: RequestBodyRepresentable { + var description: String? { "Update an existent user in the store" } + var required: Bool { false } + var contentMap: ContentMap { + [ + .json: Content(UserSchema().reference()), + .xml: Content(UserSchema().reference()), + .form: Content(UserSchema().reference()), + ] + } + } + + struct CreateWithListRequestBody: RequestBodyRepresentable { + var required: Bool { false } + var contentMap: ContentMap { + [ + .json: Content(UserArraySchema()) + ] + } + } + + struct UserArrayComponentRequestBody: RequestBodyRepresentable { + var openAPIIdentifier: String { "UserArray" } + var description: String? { "List of user object" } + var required: Bool { false } + var contentMap: ContentMap { + [ + .json: Content(UserArraySchema()) + ] + } + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/User/User+Responses.swift b/Tests/FeatherOpenAPITests/Petstore/User/User+Responses.swift new file mode 100644 index 0000000..3fd2951 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/User/User+Responses.swift @@ -0,0 +1,46 @@ +// +// User+Responses.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI +import OpenAPIKit30 + +extension Petstore.User { + + struct UserResponse: ResponseRepresentable { + let description: String + var contentMap: ContentMap { + [ + .json: Content(UserSchema().reference()), + .xml: Content(UserSchema().reference()), + ] + } + } + + struct LoginResponse: ResponseRepresentable { + var description: String { "successful operation" } + var headerMap: HeaderMap { + [ + "X-Rate-Limit": RateLimitHeader(), + "X-Expires-After": ExpiresAfterHeader(), + ] + } + var contentMap: ContentMap { + [ + .xml: Content(LoginResponseSchema()), + .json: Content(LoginResponseSchema()), + ] + } + } + + struct LoginResponseSchema: StringSchemaRepresentable { + } + + struct EmptyResponse: ResponseRepresentable { + let description: String + var contentMap: ContentMap { [:] } + } +} diff --git a/Tests/FeatherOpenAPITests/Petstore/User/User+Schemas.swift b/Tests/FeatherOpenAPITests/Petstore/User/User+Schemas.swift index 5cee0b2..4e26dd6 100644 --- a/Tests/FeatherOpenAPITests/Petstore/User/User+Schemas.swift +++ b/Tests/FeatherOpenAPITests/Petstore/User/User+Schemas.swift @@ -11,39 +11,48 @@ import OpenAPIKit30 extension Petstore.User { struct UserIdSchema: Int64SchemaRepresentable { - var example: Int64? { 0 } + var example: Int64? { 10 } + var required: Bool { false } } struct UserNameSchema: StringSchemaRepresentable { var example: String? { "theUser" } + var required: Bool { false } } struct UserFirstNameSchema: StringSchemaRepresentable { var example: String? { "John" } + var required: Bool { false } } struct UserLastNameSchema: StringSchemaRepresentable { var example: String? { "James" } + var required: Bool { false } } struct UserEmailSchema: StringSchemaRepresentable { var example: String? { "john@email.com" } + var required: Bool { false } } struct UserPasswordSchema: StringSchemaRepresentable { var example: String? { "12345" } + var required: Bool { false } } struct UserPhoneSchema: StringSchemaRepresentable { var example: String? { "12345" } + var required: Bool { false } } struct UserStatusSchema: Int32SchemaRepresentable { var description: String? { "User Status" } var example: Int32? { 1 } + var required: Bool { false } } struct UserSchema: ObjectSchemaRepresentable { + var openAPIIdentifier: String { "User" } var propertyMap: SchemaMap { [ "id": UserIdSchema(), @@ -57,4 +66,19 @@ extension Petstore.User { ] } } + + struct UsernameSchema: StringSchemaRepresentable { + } + + struct LoginUsernameSchema: StringSchemaRepresentable { + var required: Bool { false } + } + + struct LoginPasswordSchema: StringSchemaRepresentable { + var required: Bool { false } + } + + struct UserArraySchema: ArraySchemaRepresentable { + var items: SchemaRepresentable? { UserSchema().reference() } + } } diff --git a/Tests/FeatherOpenAPITests/Petstore/User/User+Tags.swift b/Tests/FeatherOpenAPITests/Petstore/User/User+Tags.swift new file mode 100644 index 0000000..9d89fe5 --- /dev/null +++ b/Tests/FeatherOpenAPITests/Petstore/User/User+Tags.swift @@ -0,0 +1,16 @@ +// +// User+Tags.swift +// feather-openapi +// +// Created by Tibor Bödecs on 2026. 01. 22.. +// + +import FeatherOpenAPI + +extension Petstore.User { + + struct UserTag: TagRepresentable { + var name: String { "user" } + var description: String? { "Operations about user" } + } +} diff --git a/Tests/FeatherOpenAPITests/PetstoreTestSuite.swift b/Tests/FeatherOpenAPITests/PetstoreTestSuite.swift index 00c61e7..374495d 100644 --- a/Tests/FeatherOpenAPITests/PetstoreTestSuite.swift +++ b/Tests/FeatherOpenAPITests/PetstoreTestSuite.swift @@ -5,9 +5,7 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // -import Foundation import OpenAPIKit30 -import OpenAPIKitCore import Testing import Yams @@ -22,11 +20,11 @@ struct PetstoreTestSuite { let document = PetstoreDocument() let encoder = YAMLEncoder() - let openAPIDocument = document.openAPIDocument() + let openAPIdoc = document.openAPIDocument() do { _ = - try openAPIDocument + try openAPIdoc .locallyDereferenced() .resolved() } @@ -35,8 +33,8 @@ struct PetstoreTestSuite { return } - let output = try encoder.encode(openAPIDocument) - - print(output) + let result = try encoder.encode(openAPIdoc) + print("---- 3.0 ----") + print(result) } } diff --git a/Tests/FeatherOpenAPITests/TodoTestSuite.swift b/Tests/FeatherOpenAPITests/TodoTestSuite.swift index 0d6c63f..82d679a 100644 --- a/Tests/FeatherOpenAPITests/TodoTestSuite.swift +++ b/Tests/FeatherOpenAPITests/TodoTestSuite.swift @@ -4,7 +4,6 @@ // // Created by Tibor Bödecs on 2026. 01. 25.. -import Foundation import OpenAPIKit import OpenAPIKit30 import OpenAPIKitCompat From 85e3241f27ecd502a1b6fb9fe0cdc72792838d89 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Mon, 26 Jan 2026 10:00:36 +0100 Subject: [PATCH 34/34] fix format --- .../FeatherOpenAPITests/Petstore/Petstore+Security.swift | 8 ++++---- .../Petstore/User/User+Operations.swift | 8 ++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Tests/FeatherOpenAPITests/Petstore/Petstore+Security.swift b/Tests/FeatherOpenAPITests/Petstore/Petstore+Security.swift index f709ad7..ab95a42 100644 --- a/Tests/FeatherOpenAPITests/Petstore/Petstore+Security.swift +++ b/Tests/FeatherOpenAPITests/Petstore/Petstore+Security.swift @@ -5,17 +5,17 @@ // Created by Tibor Bödecs on 2026. 01. 22.. // +import FeatherOpenAPI +import OpenAPIKit30 + #if canImport(FoundationEssentials) import FoundationEssentials #else import Foundation #endif -import FeatherOpenAPI -import OpenAPIKit30 - struct PetstoreAuthSecurityScheme: SecuritySchemeRepresentable { - + var type: OpenAPI.SecurityScheme.SecurityType { .oauth2( flows: .init( diff --git a/Tests/FeatherOpenAPITests/Petstore/User/User+Operations.swift b/Tests/FeatherOpenAPITests/Petstore/User/User+Operations.swift index 397000f..5f57f1e 100644 --- a/Tests/FeatherOpenAPITests/Petstore/User/User+Operations.swift +++ b/Tests/FeatherOpenAPITests/Petstore/User/User+Operations.swift @@ -30,7 +30,9 @@ extension Petstore.User { struct CreateWithListOperation: OperationRepresentable { var tags: [TagRepresentable] { [UserTag()] } var summary: String? { "Creates list of users with given input array." } - var description: String? { "Creates list of users with given input array." } + var description: String? { + "Creates list of users with given input array." + } var operationId: String? { "createUsersWithListInput" } var requestBody: RequestBodyRepresentable? { CreateWithListRequestBody() @@ -58,7 +60,9 @@ extension Petstore.User { var responseMap: ResponseMap { [ 200: LoginResponse(), - 400: EmptyResponse(description: "Invalid username/password supplied"), + 400: EmptyResponse( + description: "Invalid username/password supplied" + ), .default: EmptyResponse(description: "Unexpected error"), ]