diff --git a/.cliff.toml b/.cliff.toml new file mode 100644 index 0000000..702629f --- /dev/null +++ b/.cliff.toml @@ -0,0 +1,181 @@ +# git-cliff ~ configuration file +# https://git-cliff.org/docs/configuration + +[changelog] +header = """ +""" + +footer = """ + +----- + +**[{{ remote.github.repo }}]({{ self::remote_url() }}) license terms** + +[![License][license-badge]][license-url] + +[license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg +[license-url]: {{ self::remote_url() }}/?tab=Apache-2.0-1-ov-file#readme + +{%- macro remote_url() -%} + https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} +""" + +body = """ +{%- if version %} +## [{{ version | trim_start_matches(pat="v") }}]({{ self::remote_url() }}/tree/{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }} +{%- else %} +## [unreleased] +{%- endif %} +{%- if message %} + {%- raw %}\n{% endraw %} +{{ message }} + {%- raw %}\n{% endraw %} +{%- endif %} +{%- if version %} + {%- if previous.version %} + +**Full Changelog**: <{{ self::remote_url() }}/compare/{{ previous.version }}...{{ version }}> + {%- endif %} +{%- else %} + {%- raw %}\n{% endraw %} +{%- endif %} + +{%- if statistics %}{% if statistics.commit_count %} + {%- raw %}\n{% endraw %} +{{ statistics.commit_count }} commits in this release. + {%- raw %}\n{% endraw %} +{%- endif %}{% endif %} +----- + +{%- for group, commits in commits | group_by(attribute="group") %} + {%- raw %}\n{% endraw %} +### {{ group | upper_first }} + {%- raw %}\n{% endraw %} + {%- for commit in commits %} + {%- if commit.remote.pr_title %} + {%- set commit_message = commit.remote.pr_title %} + {%- else %} + {%- set commit_message = commit.message %} + {%- endif %} +* {{ commit_message | split(pat="\n") | first | trim }} + {%- if commit.remote.username %} +{%- raw %} {% endraw %}by [@{{ commit.remote.username }}](https://github.com/{{ commit.remote.username }}) + {%- endif %} + {%- if commit.remote.pr_number %} +{%- raw %} {% endraw %}in [#{{ commit.remote.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.remote.pr_number }}) + {%- endif %} +{%- raw %} {% endraw %}[...]({{ self::remote_url() }}/commit/{{ commit.id }}) + {%- endfor %} +{%- endfor %} + +{%- if github %} +{%- raw %}\n{% endraw -%} + {%- set all_contributors = github.contributors | length %} + {%- if github.contributors | filter(attribute="username", value="dependabot[bot]") | length < all_contributors %} +----- + +### People who contributed to this release + {% endif %} + {%- for contributor in github.contributors | filter(attribute="username") | sort(attribute="username") %} + {%- if contributor.username != "dependabot[bot]" and contributor.username != "github-actions[bot]" %} +* [@{{ contributor.username }}](https://github.com/{{ contributor.username }}) + {%- endif %} + {%- endfor %} + + {% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %} +----- + {%- raw %}\n{% endraw %} + +### New Contributors + {%- endif %} + + {%- for contributor in github.contributors | filter(attribute="is_first_time", value=true) %} + {%- if contributor.username != "dependabot[bot]" and contributor.username != "github-actions[bot]" %} +* @{{ contributor.username }} made their first contribution + {%- if contributor.pr_number %} + in [#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \ + {%- endif %} + {%- endif %} + {%- endfor %} +{%- endif %} + +{%- raw %}\n{% endraw %} + +{%- macro remote_url() -%} + https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} +""" +# Remove leading and trailing whitespaces from the changelog's body. +trim = true +# Render body even when there are no releases to process. +render_always = true +# An array of regex based postprocessors to modify the changelog. +postprocessors = [ + # Replace the placeholder with a URL. + #{ pattern = '', replace = "https://github.com/orhun/git-cliff" }, +] +# output file path +# output = "test.md" + +[git] +# Parse commits according to the conventional commits specification. +# See https://www.conventionalcommits.org +conventional_commits = false +# Exclude commits that do not match the conventional commits specification. +filter_unconventional = false +# Require all commits to be conventional. +# Takes precedence over filter_unconventional. +require_conventional = false +# Split commits on newlines, treating each line as an individual commit. +split_commits = false +# An array of regex based parsers to modify commit messages prior to further processing. +commit_preprocessors = [ + # Replace issue numbers with link templates to be updated in `changelog.postprocessors`. + #{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))"}, + # Check spelling of the commit message using https://github.com/crate-ci/typos. + # If the spelling is incorrect, it will be fixed automatically. + #{ pattern = '.*', replace_command = 'typos --write-changes -' } +] +# Prevent commits that are breaking from being excluded by commit parsers. +protect_breaking_commits = false +# An array of regex based parsers for extracting data from the commit message. +# Assigns commits to groups. +# Optionally sets the commit's scope and can decide to exclude commits from further processing. +commit_parsers = [ + { message = "^[Cc]hore\\([Rr]elease\\): prepare for", skip = true }, + { message = "(^[Mm]erge)|([Mm]erge conflict)", skip = true }, + { field = "author.name", pattern = "dependabot*", group = "Updates" }, + { message = "([Ss]ecurity)|([Vv]uln)", group = "Security" }, + { body = "(.*[Ss]ecurity)|([Vv]uln)", group = "Security" }, + { message = "([Cc]hore\\(lint\\))|(style)|(lint)|(codeql)|(golangci)", group = "Code quality" }, + { message = "(^[Dd]oc)|((?i)readme)|(badge)|(typo)|(documentation)", group = "Documentation" }, + { message = "(^[Ff]eat)|(^[Ee]nhancement)", group = "Implemented enhancements" }, + { message = "(^ci)|(\\(ci\\))|(fixup\\s+ci)|(fix\\s+ci)|(license)|(example)", group = "Miscellaneous tasks" }, + { message = "^test", group = "Testing" }, + { message = "(^fix)|(panic)", group = "Fixed bugs" }, + { message = "(^refact)|(rework)", group = "Refactor" }, + { message = "(^[Pp]erf)|(performance)", group = "Performance" }, + { message = "(^[Cc]hore)", group = "Miscellaneous tasks" }, + { message = "^[Rr]evert", group = "Reverted changes" }, + { message = "(upgrade.*?go)|(go\\s+version)", group = "Updates" }, + { message = ".*", group = "Other" }, +] +# Exclude commits that are not matched by any commit parser. +filter_commits = false +# An array of link parsers for extracting external references, and turning them into URLs, using regex. +link_parsers = [] +# Include only the tags that belong to the current branch. +use_branch_tags = false +# Order releases topologically instead of chronologically. +topo_order = false +# Order releases topologically instead of chronologically. +topo_order_commits = true +# Order of commits in each group/release within the changelog. +# Allowed values: newest, oldest +sort_commits = "newest" +# Process submodules commits +recurse_submodules = false + +#[remote.github] +#owner = "go-openapi" diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 7dea424..85707f7 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,114 +1,211 @@ ## Contribution Guidelines +You'll find below general guidelines, which mostly correspond to standard practices for open sourced repositories. + +>**TL;DR** +> +> If you're already an experienced go developer on github, then you should just feel at home with us +> and you may well skip the rest of this document. +> +> You'll essentially find the usual guideline for a go library project on github. + +These guidelines are general to all libraries published on github by the `go-openapi` organization. + +You'll find more detailed (or repo-specific) instructions in the [maintainer's docs](../docs). + +## How can I contribute? + +There are many ways in which you can contribute. Here are a few ideas: + + * Reporting Issues / Bugs + * Suggesting Improvements + * Code + * bug fixes and new features that are within the main project scope + * improving test coverage + * addressing code quality issues + * Documentation + * Art work that makes the project look great + +## Questions & issues + +### Asking questions + +You may inquire about anything about this library by reporting a "Question" issue on github. + +### Reporting issues + +Reporting a problem with our libraries _is_ a valuable contribution. + +You can do this on the github issues page of this repository. + +Please be as specific as possible when describing your issue. + +Whenever relevant, please provide information about your environment (go version, OS). + +Adding a code snippet to reproduce the issue is great, and as a big time saver for maintainers. + +### Triaging issues + +You can help triage issues which may include: + +* reproducing bug reports +* asking for important information, such as version numbers or reproduction instructions +* answering questions and sharing your insight in issue comments + +## Code contributions + ### Pull requests are always welcome -We are always thrilled to receive pull requests, and do our best to -process them as fast as possible. Not sure if that typo is worth a pull -request? Do it! We will appreciate it. +We are always thrilled to receive pull requests, and we do our best to +process them as fast as possible. + +Not sure if that typo is worth a pull request? Do it! We will appreciate it. + +If your pull request is not accepted on the first try, don't be discouraged! +If there's a problem with the implementation, hopefully you received feedback on what to improve. + +If you have a lot of ideas or a lot of issues to solve, try to refrain a bit and post focused +pull requests. +Think that they must be reviewed by a maintainer and it is easy to lost track of things on big PRs. + +We're trying very hard to keep the go-openapi packages lean and focused. +These packages constitute a toolkit: it won't do everything for everybody out of the box, +but everybody can use it to do just about everything related to OpenAPI. + +This means that we might decide against incorporating a new feature. + +However, there might be a way to implement that feature *on top of* our libraries. + +### Environment + +You just need a `go` compiler to be installed. No special tools are needed to work with our libraries. + +The go compiler version required is always the old stable (latest minor go version - 1). -If your pull request is not accepted on the first try, don't be -discouraged! If there's a problem with the implementation, hopefully you -received feedback on what to improve. +If you're already used to work with `go` you should already have everything in place. -We're trying very hard to keep go-swagger lean and focused. We don't want it -to do everything for everybody. This means that we might decide against -incorporating a new feature. However, there might be a way to implement -that feature *on top of* go-swagger. +Although not required, you'll be certainly more productive with a local installation of `golangci-lint`, +the meta-linter our CI uses. +If you don't have it, you may install it like so: + +```sh +go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest +``` ### Conventions -Fork the repo and make changes on your fork in a feature branch: +#### Git flow + +Fork the repo and make changes to your fork in a feature branch. + +To submit a pull request, push your branch to your fork (e.g. `upstream` remote): +github will propose to open a pull request on the original repository. + +Typically you'd follow some common naming conventions: + +- if it's a bugfix branch, name it `fix/XXX-something`where XXX is the number of the + issue on github +- if it's a feature branch, create an enhancement issue to announce your + intentions, and name it `feature/XXX-something` where XXX is the number of the issue. + +> NOTE: we don't enforce naming conventions on branches: it's your fork after all. + +#### Tests + +Submit unit tests for your changes. + +Go has a great built-in test framework ; use it! + +Take a look at existing tests for inspiration, and run the full test suite on your branch +before submitting a pull request. + +Our CI measures test coverage and the test coverage of every patch. +Although not a blocking step - because there are so many special cases - +this is an indicator that maintainers consider when approving a PR. + +Please try your best to cover about 80% of your patch. + +#### Code style -- If it's a bugfix branch, name it XXX-something where XXX is the number of the - issue -- If it's a feature branch, create an enhancement issue to announce your - intentions, and name it XXX-something where XXX is the number of the issue. +You may read our stance on code style [there](../docs/STYLE.md). -Submit unit tests for your changes. Go has a great test framework built in; use -it! Take a look at existing tests for inspiration. Run the full test suite on -your branch before submitting a pull request. +#### Documentation -Update the documentation when creating or modifying features. Test -your documentation changes for clarity, concision, and correctness, as -well as a clean documentation build. See ``docs/README.md`` for more -information on building the docs and how docs get released. +Don't forget to update the documentation when creating or modifying features. -Write clean code. Universally formatted code promotes ease of writing, reading, -and maintenance. Always run `gofmt -s -w file.go` on each changed file before -committing your changes. Most editors have plugins that do this automatically. +Most documentation for this library is directly found in code as comments for godoc. + +The documentation for the go-openapi packages is published on the public go docs site: + + + +Check your documentation changes for clarity, concision, and correctness. + +If you want to assess the rendering of your changes when published to `pkg.go.dev`, you may +want to install the `pkgsite` tool proposed by `golang.org`. + +```sh +go install golang.org/x/pkgsite/cmd/pkgsite@latest +``` + +Then run on the repository folder: +```sh +pkgsite . +``` + +This wil run a godoc server locally where you may see the documentation generated from your local repository. + +#### Commit messages Pull requests descriptions should be as clear as possible and include a reference to all the issues that they address. Pull requests must not contain commits from other users or branches. -Commit messages must start with a capitalized and short summary (max. 50 -chars) written in the imperative, followed by an optional, more detailed -explanatory text which is separated from the summary by an empty line. +Commit messages are not required to follow the "conventional commit" rule, but it's certainly a good +thing to follow this guidelinea (e.g. "fix: blah blah", "ci: did this", "feat: did that" ...). -Code review comments may be added to your pull request. Discuss, then make the -suggested modifications and push additional commits to your feature branch. Be -sure to post a comment after pushing. The new commits will show up in the pull -request automatically, but the reviewers will not be notified unless you -comment. +The title in your commit message is used directly to produce our release notes: try to keep them neat. -Before the pull request is merged, make sure that you squash your commits into -logical units of work using `git rebase -i` and `git push -f`. After every -commit the test suite should be passing. Include documentation changes in the -same commit so that a revert would remove all traces of the feature or fix. +The commit message body should detail your changes. -Commits that fix or close an issue should include a reference like `Closes #XXX` -or `Fixes #XXX`, which will automatically close the issue when merged. +If an issue should be closed by a commit, please add this reference in the commit body: -### Sign your work +``` +* fixes #{issue number} +``` -The sign-off is a simple line at the end of the explanation for the -patch, which certifies that you wrote it or otherwise have the right to -pass it on as an open-source patch. The rules are pretty simple: if you -can certify the below (from -[developercertificate.org](http://developercertificate.org/)): +#### Code review -``` -Developer Certificate of Origin -Version 1.1 +Code review comments may be added to your pull request. -Copyright (C) 2004, 2006 The Linux Foundation and its contributors. -660 York Street, Suite 102, -San Francisco, CA 94110 USA +Discuss, then make the suggested modifications and push additional commits to your feature branch. -Everyone is permitted to copy and distribute verbatim copies of this -license document, but changing it is not allowed. +Be sure to post a comment after pushing. The new commits will show up in the pull +request automatically, but the reviewers will not be notified unless you comment. +Before the pull request is merged, +**make sure that you squash your commits into logical units of work** +using `git rebase -i` and `git push -f`. -Developer's Certificate of Origin 1.1 +After every commit the test suite should be passing. -By making a contribution to this project, I certify that: +Include documentation changes in the same commit so that a revert would remove all traces of the feature or fix. -(a) The contribution was created in whole or in part by me and I - have the right to submit it under the open source license - indicated in the file; or +#### Sign your work -(b) The contribution is based upon previous work that, to the best - of my knowledge, is covered under an appropriate open source - license and I have the right under that license to submit that - work with modifications, whether created in whole or in part - by me, under the same open source license (unless I am - permitted to submit under a different license), as indicated - in the file; or +The sign-off is a simple line at the end of your commit message, +which certifies that you wrote it or otherwise have the right to +pass it on as an open-source patch. -(c) The contribution was provided directly to me by some other - person who certified (a), (b) or (c) and I have not modified - it. +We require the simple DCO below with an email signing your commit. +PGP-signed commit are greatly appreciated but not required. -(d) I understand and agree that this project and the contribution - are public and that a record of the contribution (including all - personal information I submit with it, including my sign-off) is - maintained indefinitely and may be redistributed consistent with - this project or the open source license(s) involved. -``` +The rules are pretty simple: -then you just add a line to every git commit message: +* read our [DCO](./DCO.md) (from [developercertificate.org](http://developercertificate.org/)) +* if you agree with these terms, then you just add a line to every git commit message Signed-off-by: Joe Smith diff --git a/.github/DCO.md b/.github/DCO.md new file mode 100644 index 0000000..e168dc4 --- /dev/null +++ b/.github/DCO.md @@ -0,0 +1,40 @@ + # Developer's Certificate of Origin + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 7dc7009..9ce426e 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -37,7 +37,7 @@ updates: patterns: - "github.com/stretchr/testify" - golang.org-dependencies: + golang-org-dependencies: patterns: - "golang.org/*" diff --git a/.github/workflows/go-test.yml b/.github/workflows/go-test.yml index e1c413e..340ce07 100644 --- a/.github/workflows/go-test.yml +++ b/.github/workflows/go-test.yml @@ -17,13 +17,13 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: stable + go-version: 1.25.5 check-latest: true cache: true - name: golangci-lint - uses: golangci/golangci-lint-action@v4 + uses: golangci/golangci-lint-action@v6 with: - version: latest + version: v1.59.1 only-new-issues: true skip-cache: true @@ -38,7 +38,7 @@ jobs: steps: - name: Run unit tests - uses: actions/setup-go@v5 + - uses: actions/setup-go@v5 with: go-version: '${{ matrix.go_version }}' check-latest: true @@ -47,9 +47,9 @@ jobs: - uses: actions/checkout@v4 - run: go test -v -race -coverprofile="coverage-${{ matrix.os }}.${{ matrix.go_version }}.out" -covermode=atomic -coverpkg=$(go list)/... ./... - - name: Upload coverage to codecov uses: codecov/codecov-action@v4 + with: files: './coverage-${{ matrix.os }}.${{ matrix.go_version }}.out' flags: '${{ matrix.go_version }}' diff --git a/.golangci.yml b/.golangci.yml index 22f8d21..fea0b52 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,61 +1,64 @@ -linters-settings: - govet: - check-shadowing: true - golint: - min-confidence: 0 - gocyclo: - min-complexity: 45 - maligned: - suggest-new: true - dupl: - threshold: 200 - goconst: - min-len: 2 - min-occurrences: 3 - +version: "2" linters: - enable-all: true + default: all disable: - - maligned - - unparam - - lll - - gochecknoinits - - gochecknoglobals + - depguard - funlen - godox - - gocognit - - whitespace - - wsl - - wrapcheck - - testpackage + - exhaustruct - nlreturn - - gomnd - - exhaustivestruct - - goerr113 - - errorlint - - nestif - - godot - - gofumpt + - nonamedreturns - paralleltest + - testpackage - tparallel - - thelper - - ifshort - - exhaustruct - varnamelen - - gci - - depguard - - errchkjson - - inamedparam - - nonamedreturns - - musttag - - ireturn - - forcetypeassert - - cyclop - # deprecated linters - - deadcode - - interfacer - - scopelint - - varcheck - - structcheck - - golint - - nosnakecase + - whitespace + - wrapcheck + - wsl + - typecheck + settings: + dupl: + threshold: 200 + goconst: + min-len: 2 + min-occurrences: 3 + cyclop: + max-complexity: 20 + gocyclo: + min-complexity: 20 + exhaustive: + default-signifies-exhaustive: true + default-case-required: true + lll: + line-length: 180 + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gofmt + - goimports + - gofumpt + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ +issues: + # Maximum issues count per one linter. + # Set to 0 to disable. + # Default: 50 + max-issues-per-linter: 0 + # Maximum count of issues with the same text. + # Set to 0 to disable. + # Default: 3 + max-same-issues: 0 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 0000000..47d6a56 --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,50 @@ +# Contributors + +- Repository: ['go-openapi/spec'] + +| Total Contributors | Total Contributions | +| --- | --- | +| 38 | 388 | + +| Username | All Time Contribution Count | All Commits | +| --- | --- | --- | +| @casualjim | 191 | https://github.com/go-openapi/spec/commits?author=casualjim | +| @fredbi | 86 | https://github.com/go-openapi/spec/commits?author=fredbi | +| @pytlesk4 | 26 | https://github.com/go-openapi/spec/commits?author=pytlesk4 | +| @kul-amr | 10 | https://github.com/go-openapi/spec/commits?author=kul-amr | +| @keramix | 10 | https://github.com/go-openapi/spec/commits?author=keramix | +| @youyuanwu | 8 | https://github.com/go-openapi/spec/commits?author=youyuanwu | +| @pengsrc | 7 | https://github.com/go-openapi/spec/commits?author=pengsrc | +| @alphacentory | 5 | https://github.com/go-openapi/spec/commits?author=alphacentory | +| @mtfelian | 4 | https://github.com/go-openapi/spec/commits?author=mtfelian | +| @Capstan | 4 | https://github.com/go-openapi/spec/commits?author=Capstan | +| @sdghchj | 4 | https://github.com/go-openapi/spec/commits?author=sdghchj | +| @databus23 | 2 | https://github.com/go-openapi/spec/commits?author=databus23 | +| @vburenin | 2 | https://github.com/go-openapi/spec/commits?author=vburenin | +| @petrkotas | 2 | https://github.com/go-openapi/spec/commits?author=petrkotas | +| @nikhita | 2 | https://github.com/go-openapi/spec/commits?author=nikhita | +| @hypnoglow | 2 | https://github.com/go-openapi/spec/commits?author=hypnoglow | +| @carvind | 2 | https://github.com/go-openapi/spec/commits?author=carvind | +| @ujjwalsh | 1 | https://github.com/go-openapi/spec/commits?author=ujjwalsh | +| @mbohlool | 1 | https://github.com/go-openapi/spec/commits?author=mbohlool | +| @j2gg0s | 1 | https://github.com/go-openapi/spec/commits?author=j2gg0s | +| @ishveda | 1 | https://github.com/go-openapi/spec/commits?author=ishveda | +| @micln | 1 | https://github.com/go-openapi/spec/commits?author=micln | +| @GlenDC | 1 | https://github.com/go-openapi/spec/commits?author=GlenDC | +| @agmikhailov | 1 | https://github.com/go-openapi/spec/commits?author=agmikhailov | +| @tgraf | 1 | https://github.com/go-openapi/spec/commits?author=tgraf | +| @zhsj | 1 | https://github.com/go-openapi/spec/commits?author=zhsj | +| @sebastien-rosset | 1 | https://github.com/go-openapi/spec/commits?author=sebastien-rosset | +| @alexandear | 1 | https://github.com/go-openapi/spec/commits?author=alexandear | +| @morlay | 1 | https://github.com/go-openapi/spec/commits?author=morlay | +| @mikedanese | 1 | https://github.com/go-openapi/spec/commits?author=mikedanese | +| @koron | 1 | https://github.com/go-openapi/spec/commits?author=koron | +| @honza | 1 | https://github.com/go-openapi/spec/commits?author=honza | +| @gbjk | 1 | https://github.com/go-openapi/spec/commits?author=gbjk | +| @faguirre1 | 1 | https://github.com/go-openapi/spec/commits?author=faguirre1 | +| @ethantkoenig | 1 | https://github.com/go-openapi/spec/commits?author=ethantkoenig | +| @sttts | 1 | https://github.com/go-openapi/spec/commits?author=sttts | +| @ChandanChainani | 1 | https://github.com/go-openapi/spec/commits?author=ChandanChainani | +| @bvwells | 1 | https://github.com/go-openapi/spec/commits?author=bvwells | + + _this file was generated by the [Contributors GitHub Action](https://github.com/github/contributors)_ diff --git a/README.md b/README.md index 7fd2810..5a877d2 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,28 @@ -# OpenAPI v2 object model [![Build Status](https://github.com/go-openapi/spec/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/spec/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/spec/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/spec) +# spec -[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) -[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/spec/master/LICENSE) -[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/spec.svg)](https://pkg.go.dev/github.com/go-openapi/spec) -[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/spec)](https://goreportcard.com/report/github.com/go-openapi/spec) + +[![Tests][test-badge]][test-url] [![Coverage][cov-badge]][cov-url] [![CI vuln scan][vuln-scan-badge]][vuln-scan-url] [![CodeQL][codeql-badge]][codeql-url] + + + +[![Release][release-badge]][release-url] [![Go Report Card][gocard-badge]][gocard-url] [![CodeFactor Grade][codefactor-badge]][codefactor-url] [![License][license-badge]][license-url] + + +[![GoDoc][godoc-badge]][godoc-url] [![Slack Channel][slack-logo]![slack-badge]][slack-url] [![go version][goversion-badge]][goversion-url] ![Top language][top-badge] ![Commits since latest release][commits-badge] -The object model for OpenAPI specification documents. +--- + +The object model for OpenAPI v2 specification documents. + +## Status + +API is stable. + +## Import this library in your project + +```cmd +go get github.com/go-openapi/spec +``` ### FAQ @@ -52,3 +69,67 @@ The object model for OpenAPI specification documents. > This `id` does not conflict with any property named `id`. > > See also https://github.com/go-openapi/spec/issues/23 + +## Change log + +See + +## References + + + +## Licensing + +This library ships under the [SPDX-License-Identifier: Apache-2.0](./LICENSE). + +## Other documentation + +* [All-time contributors](./CONTRIBUTORS.md) +* [Contributing guidelines](.github/CONTRIBUTING.md) +* [Maintainers documentation](docs/MAINTAINERS.md) +* [Code style](docs/STYLE.md) + +## Cutting a new release + +Maintainers can cut a new release by either: + +* running [this workflow](https://github.com/go-openapi/spec/actions/workflows/bump-release.yml) +* or pushing a semver tag + * signed tags are preferred + * The tag message is prepended to release notes + + +[test-badge]: https://github.com/go-openapi/spec/actions/workflows/go-test.yml/badge.svg +[test-url]: https://github.com/go-openapi/spec/actions/workflows/go-test.yml +[cov-badge]: https://codecov.io/gh/go-openapi/spec/branch/master/graph/badge.svg +[cov-url]: https://codecov.io/gh/go-openapi/spec +[vuln-scan-badge]: https://github.com/go-openapi/spec/actions/workflows/scanner.yml/badge.svg +[vuln-scan-url]: https://github.com/go-openapi/spec/actions/workflows/scanner.yml +[codeql-badge]: https://github.com/go-openapi/spec/actions/workflows/codeql.yml/badge.svg +[codeql-url]: https://github.com/go-openapi/spec/actions/workflows/codeql.yml + +[release-badge]: https://badge.fury.io/gh/go-openapi%2Fspec.svg +[release-url]: https://badge.fury.io/gh/go-openapi%2Fspec +[gomod-badge]: https://badge.fury.io/go/github.com%2Fgo-openapi%2Fspec.svg +[gomod-url]: https://badge.fury.io/go/github.com%2Fgo-openapi%2Fspec + +[gocard-badge]: https://goreportcard.com/badge/github.com/go-openapi/spec +[gocard-url]: https://goreportcard.com/report/github.com/go-openapi/spec +[codefactor-badge]: https://img.shields.io/codefactor/grade/github/go-openapi/spec +[codefactor-url]: https://www.codefactor.io/repository/github/go-openapi/spec + +[doc-badge]: https://img.shields.io/badge/doc-site-blue?link=https%3A%2F%2Fgoswagger.io%2Fgo-openapi%2F +[doc-url]: https://goswagger.io/go-openapi +[godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/spec +[godoc-url]: http://pkg.go.dev/github.com/go-openapi/spec +[slack-logo]: https://a.slack-edge.com/e6a93c1/img/icons/favicon-32.png +[slack-badge]: https://img.shields.io/badge/slack-blue?link=https%3A%2F%2Fgoswagger.slack.com%2Farchives%2FC04R30YM +[slack-url]: https://goswagger.slack.com/archives/C04R30YMU + +[license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg +[license-url]: https://github.com/go-openapi/spec/?tab=Apache-2.0-1-ov-file#readme + +[goversion-badge]: https://img.shields.io/github/go-mod/go-version/go-openapi/spec +[goversion-url]: https://github.com/go-openapi/spec/blob/master/go.mod +[top-badge]: https://img.shields.io/github/languages/top/go-openapi/spec +[commits-badge]: https://img.shields.io/github/commits-since/go-openapi/spec/latest diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..2a7b6f0 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +This policy outlines the commitment and practices of the go-openapi maintainers regarding security. + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 0.22.x | :white_check_mark: | + +## Reporting a vulnerability + +If you become aware of a security vulnerability that affects the current repository, +please report it privately to the maintainers. + +Please follow the instructions provided by github to +[Privately report a security vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability). + +TL;DR: on Github, navigate to the project's "Security" tab then click on "Report a vulnerability". diff --git a/auth_test.go b/auth_test.go index a49340c..4f25a34 100644 --- a/auth_test.go +++ b/auth_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -76,7 +65,6 @@ func TestSerialization_AuthSerialization(t *testing.T) { } func TestSerialization_AuthDeserialization(t *testing.T) { - assertParsesJSON(t, `{"type":"basic"}`, BasicAuth()) assertParsesJSON( @@ -131,5 +119,4 @@ func TestSerialization_AuthDeserialization(t *testing.T) { `{"authorizationUrl":"http://foo.com/authorization","flow":"accessCode","scopes":{"email":"read your email"},`+ `"tokenUrl":"http://foo.com/token","type":"oauth2"}`, auth4) - } diff --git a/cache.go b/cache.go index 122993b..cd38dcb 100644 --- a/cache.go +++ b/cache.go @@ -1,40 +1,28 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec import ( + "maps" "sync" ) // ResolutionCache a cache for resolving urls type ResolutionCache interface { - Get(string) (interface{}, bool) - Set(string, interface{}) + Get(key string) (any, bool) + Set(key string, value any) } type simpleCache struct { lock sync.RWMutex - store map[string]interface{} + store map[string]any } func (s *simpleCache) ShallowClone() ResolutionCache { - store := make(map[string]interface{}, len(s.store)) + store := make(map[string]any, len(s.store)) s.lock.RLock() - for k, v := range s.store { - store[k] = v - } + maps.Copy(store, s.store) s.lock.RUnlock() return &simpleCache{ @@ -43,7 +31,7 @@ func (s *simpleCache) ShallowClone() ResolutionCache { } // Get retrieves a cached URI -func (s *simpleCache) Get(uri string) (interface{}, bool) { +func (s *simpleCache) Get(uri string) (any, bool) { s.lock.RLock() v, ok := s.store[uri] @@ -52,7 +40,7 @@ func (s *simpleCache) Get(uri string) (interface{}, bool) { } // Set caches a URI -func (s *simpleCache) Set(uri string, data interface{}) { +func (s *simpleCache) Set(uri string, data any) { s.lock.Lock() s.store[uri] = data s.lock.Unlock() @@ -80,7 +68,7 @@ func initResolutionCache() { } func defaultResolutionCache() *simpleCache { - return &simpleCache{store: map[string]interface{}{ + return &simpleCache{store: map[string]any{ "http://swagger.io/v2/schema.json": MustLoadSwagger20Schema(), "http://json-schema.org/draft-04/schema": MustLoadJSONSchemaDraft04(), }} diff --git a/cache_test.go b/cache_test.go index 8d6d274..1ef15db 100644 --- a/cache_test.go +++ b/cache_test.go @@ -1,9 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package spec import ( "testing" - "github.com/stretchr/testify/assert" + "github.com/go-openapi/testify/v2/assert" ) func TestDefaultResolutionCache(t *testing.T) { diff --git a/circular_test.go b/circular_test.go index c064b61..cc607c2 100644 --- a/circular_test.go +++ b/circular_test.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package spec import ( @@ -9,8 +12,8 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) func TestExpandCircular_Issue3(t *testing.T) { @@ -263,7 +266,12 @@ func TestCircular_RemoteExpandAzure(t *testing.T) { require.NotNil(t, pth1) // check expected remaining $ref - assertRefInJSONRegexp(t, jazon, `^(#/definitions/)|(networkInterface.json#/definitions/)|(networkSecurityGroup.json#/definitions/)|(network.json#/definitions)|(virtualNetworkTap.json#/definitions/)|(virtualNetwork.json#/definitions/)|(privateEndpoint.json#/definitions/)|(\./examples/)`) + assertRefInJSONRegexp(t, jazon, + `^(#/definitions/)|(networkInterface.json#/definitions/)|`+ + `(networkSecurityGroup.json#/definitions/)|(network.json#/definitions)|`+ + `(virtualNetworkTap.json#/definitions/)|(virtualNetwork.json#/definitions/)|`+ + `(privateEndpoint.json#/definitions/)|(\./examples/)`, + ) // check all $ref resolve in the expanded root // (filter out the remaining $ref in x-ms-example extensions, which are not expanded) diff --git a/contact_info.go b/contact_info.go index 2f7bb21..fafe639 100644 --- a/contact_info.go +++ b/contact_info.go @@ -1,23 +1,12 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec import ( "encoding/json" - "github.com/go-openapi/swag" + "github.com/go-openapi/swag/jsonutils" ) // ContactInfo contact information for the exposed API. @@ -53,5 +42,5 @@ func (c ContactInfo) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return swag.ConcatJSON(b1, b2), nil + return jsonutils.ConcatJSON(b1, b2), nil } diff --git a/contact_info_test.go b/contact_info_test.go index fedd81a..6159f75 100644 --- a/contact_info_test.go +++ b/contact_info_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -18,8 +7,8 @@ import ( "encoding/json" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) const contactInfoJSON = `{ @@ -33,15 +22,15 @@ var contactInfo = ContactInfo{ContactInfoProps: ContactInfoProps{ Name: "wordnik api team", URL: "http://developer.wordnik.com", Email: "some@mailayada.dkdkd", -}, VendorExtensible: VendorExtensible{Extensions: map[string]interface{}{"x-teams": "test team"}}} +}, VendorExtensible: VendorExtensible{Extensions: map[string]any{"x-teams": "test team"}}} func TestIntegrationContactInfo(t *testing.T) { b, err := json.MarshalIndent(contactInfo, "", "\t") require.NoError(t, err) - assert.Equal(t, contactInfoJSON, string(b)) + assert.JSONEq(t, contactInfoJSON, string(b)) actual := ContactInfo{} err = json.Unmarshal([]byte(contactInfoJSON), &actual) require.NoError(t, err) - assert.EqualValues(t, contactInfo, actual) + assert.Equal(t, contactInfo, actual) } diff --git a/debug.go b/debug.go index fc889f6..a08422d 100644 --- a/debug.go +++ b/debug.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -27,10 +16,8 @@ import ( // It enables a more verbose logging of this package. var Debug = os.Getenv("SWAGGER_DEBUG") != "" -var ( - // specLogger is a debug logger for this package - specLogger *log.Logger -) +// specLogger is a debug logger for this package. +var specLogger *log.Logger //nolint:gochecknoglobals func init() { debugOptions() @@ -40,7 +27,7 @@ func debugOptions() { specLogger = log.New(os.Stdout, "spec:", log.LstdFlags) } -func debugLog(msg string, args ...interface{}) { +func debugLog(msg string, args ...any) { // A private, trivial trace logger, based on go-openapi/spec/expander.go:debugLog() if Debug { _, file1, pos1, _ := runtime.Caller(1) diff --git a/debug_test.go b/debug_test.go index c74fc28..bc83691 100644 --- a/debug_test.go +++ b/debug_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -19,15 +8,14 @@ import ( "sync" "testing" - "github.com/stretchr/testify/assert" + "github.com/go-openapi/testify/v2/assert" ) -var ( - logMutex = &sync.Mutex{} -) +var logMutex = &sync.Mutex{} //nolint:gochecknoglobals func TestDebug(t *testing.T) { - tmpFile, _ := os.CreateTemp("", "debug-test") + // usetesting linter disabled until https://github.com/golang/go/issues/71544 is fixed for windows + tmpFile, _ := os.CreateTemp("", "debug-test") //nolint:usetesting tmpName := tmpFile.Name() defer func() { Debug = false diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..04eea35 --- /dev/null +++ b/doc.go @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +// Package spec exposes an object model for OpenAPIv2 specifications (swagger). +// +// The exposed data structures know how to serialize to and deserialize from JSON. +package spec diff --git a/docs/MAINTAINERS.md b/docs/MAINTAINERS.md new file mode 100644 index 0000000..6c15d12 --- /dev/null +++ b/docs/MAINTAINERS.md @@ -0,0 +1,159 @@ +# Maintainer's guide + +## Repo structure + +Single go module. + +> **NOTE** +> +> Some `go-openapi` repos are mono-repos with multiple modules, +> with adapted CI workflows. + +## Repo configuration + +* default branch: master +* protected branches: master +* branch protection rules: + * require pull requests and approval + * required status checks: + - DCO (simple email sign-off) + - Lint + - tests completed +* auto-merge enabled (used for dependabot updates) + +## Continuous Integration + +### Code Quality checks + +* meta-linter: golangci-lint +* linter config: [`.golangci.yml`](../.golangci.yml) (see our [posture](./STYLE.md) on linters) + +* Code quality assessment: [CodeFactor](https://www.codefactor.io/dashboard) +* Code quality badges + * go report card: + * CodeFactor: + +> **NOTES** +> +> codefactor inherits roles from github. There is no need to create a dedicated account. +> +> The codefactor app is installed at the organization level (`github.com/go-openapi`). +> +> There is no special token to setup in github for CI usage. + +### Testing + +* Test reports + * Uploaded to codecov: +* Test coverage reports + * Uploaded to codecov: + +* Fuzz testing + * Fuzz tests are handled separately by CI and may reuse a cached version of the fuzzing corpus. + At this moment, cache may not be shared between feature branches or feature branch and master. + The minimized corpus produced on failure is uploaded as an artifact and should be added manually + to `testdata/fuzz/...`. + +Coverage threshold status is informative and not blocking. +This is because the thresholds are difficult to tune and codecov oftentimes reports false negatives +or may fail to upload coverage. + +All tests use our fork of `stretchr/testify`: `github.com/go-openapi/testify`. +This allows for minimal test dependencies. + +> **NOTES** +> +> codecov inherits roles from github. There is no need to create a dedicated account. +> However, there is only 1 maintainer allowed to be the admin of the organization on codecov +> with their free plan. +> +> The codecov app is installed at the organization level (`github.com/go-openapi`). +> +> There is no special token to setup in github for CI usage. +> A organization-level token used to upload coverage and test reports is managed at codecov: +> no setup is required on github. + +### Automated updates + +* dependabot + * configuration: [`dependabot.yaml`](../.github/dependabot.yaml) + + Principle: + + * codecov applies updates and security patches to the github-actions and golang ecosystems. + * all updates from "trusted" dependencies (github actions, golang.org packages, go-openapi packages + are auto-merged if they successfully pass CI. + +* go version udpates + + Principle: + + * we support the 2 latest minor versions of the go compiler (`stable`, `oldstable`) + * `go.mod` should be updated (manually) whenever there is a new go minor release + (e.g. every 6 months). + +* contributors + * a [`CONTRIBUTORS.md`](../CONTRIBUTORS.md) file is updated weekly, with all-time contributors to the repository + * the `github-actions[bot]` posts a pull request to do that automatically + * at this moment, this pull request is not auto-approved/auto-merged (bot cannot approve its own PRs) + +### Vulnerability scanners + +There are 3 complementary scanners - obviously, there is some overlap, but each has a different focus. + +* github `CodeQL` +* `trivy` +* `govulnscan` + +None of these tools require an additional account or token. + +Github CodeQL configuration is set to "Advanced", so we may collect a CI status for this check (e.g. for badges). + +Scanners run on every commit to master and at least once a week. + +Reports are centralized in github security reports for code scanning tools. + +## Releases + +The release process is minimalist: + +* push a semver tag (i.e v{major}.{minor}.{patch}) to the master branch. +* the CI handles this to generate a github release with release notes + +* release notes generator: git-cliff +* configuration: [`cliff.toml`](../.cliff.toml) + +Tags are preferably PGP-signed. + +The tag message introduces the release notes (e.g. a summary of this release). + +The release notes generator does not assume that commits are necessarily "conventional commits". + +## Other files + +Standard documentation: + +* [`CONTRIBUTING.md`](../.github/CONTRIBUTING.md) guidelines +* [`DCO.md`](../.github/DCO.md) terms for first-time contributors to read +* [`CODE_OF_CONDUCT.md`](../CODE_OF_CONDUCT.md) +* [`SECURIY.md`](../SECURITY.md) policy: how to report vulnerabilities privately +* [`LICENSE`](../LICENSE) terms + + +Reference documentation (released): + +* [godoc](https://pkg.go.dev/github.com/go-openapi/spec) + +## TODOs & other ideas + +A few things remain ahead to ease a bit a maintainer's job: + +* [x] reuse CI workflows (e.g. in `github.com/go-openapi/workflows`) +* [x] reusable actions with custom tools pinned (e.g. in `github.com/go-openapi/gh-actions`) +* open-source license checks +* [x] auto-merge for CONTRIBUTORS.md (requires a github app to produce tokens) +* [ ] more automated code renovation / relinting work (possibly built with CLAUDE) (ongoing) +* organization-level documentation web site +* ... diff --git a/docs/STYLE.md b/docs/STYLE.md new file mode 100644 index 0000000..056fdb5 --- /dev/null +++ b/docs/STYLE.md @@ -0,0 +1,83 @@ +# Coding style at `go-openapi` + +> **TL;DR** +> +> Let's be honest: at `go-openapi` and `go-swagger` we've never been super-strict on code style etc. +> +> But perhaps now (2025) is the time to adopt a different stance. + +Even though our repos have been early adopters of `golangci-lint` years ago +(we used some other metalinter before), our decade-old codebase is only realigned to new rules from time to time. + +Now go-openapi and go-swagger make up a really large codebase, which is taxing to maintain and keep afloat. + +Code quality and the harmonization of rules have thus become things that we need now. + +## Meta-linter + +Universally formatted go code promotes ease of writing, reading, and maintenance. + +You should run `golangci-lint run` before committing your changes. + +Many editors have plugins that do that automatically. + +> We use the `golangci-lint` meta-linter. The configuration lies in [`.golangci.yml`](../.golangci.yml). +> You may read for additional reference. + +## Linting rules posture + +Thanks to go's original design, we developers don't have to waste much time arguing about code figures of style. + +However, the number of available linters has been growing to the point that we need to pick a choice. + +We enable all linters published by `golangci-lint` by default, then disable a few ones. + +Here are the reasons why they are disabled (update: Nov. 2025, `golangci-lint v2.6.1`): + +```yaml + disable: + - depguard # we don't want to configure rules to constrain import. That's the reviewer's job + - exhaustruct # we don't want to configure regexp's to check type name. That's the reviewer's job + - funlen # we accept cognitive complexity as a meaningful metric, but function length is relevant + - godox # we don't see any value in forbidding TODO's etc in code + - nlreturn # we usually apply this "blank line" rule to make code less compact. We just don't want to enforce it + - nonamedreturns # we don't see any valid reason why we couldn't used named returns + - noinlineerr # there is no value added forbidding inlined err + - paralleltest # we like parallel tests. We just don't want them to be enforced everywhere + - recvcheck # we like the idea of having pointer and non-pointer receivers + - testpackage # we like test packages. We just don't want them to be enforced everywhere + - tparallel # see paralleltest + - varnamelen # sometimes, we like short variables. The linter doesn't catch cases when a short name is good + - whitespace # no added value + - wrapcheck # although there is some sense with this linter's general idea, it produces too much noise + - wsl # no added value. Noise + - wsl_v5 # no added value. Noise +``` + +As you may see, we agree with the objective of most linters, at least the principle they are supposed to enforce. +But all linters do not support fine-grained tuning to tolerate some cases and not some others. + +When this is possible, we enable linters with relaxed constraints: + +```yaml + settings: + dupl: + threshold: 200 # in a older code base such as ours, we have to be tolerant with a little redundancy + # Hopefully, we'll be able to gradually get rid of those. + goconst: + min-len: 2 + min-occurrences: 3 + cyclop: + max-complexity: 20 # the default is too low for most of our functions. 20 is a nicer trade-off + gocyclo: + min-complexity: 20 + exhaustive: # when using default in switch, this should be good enough + default-signifies-exhaustive: true + default-case-required: true + lll: + line-length: 180 # we just want to avoid extremely long lines. + # It is no big deal if a line or two don't fit on your terminal. +``` + +Final note: since we have switched to a forked version of `stretchr/testify`, +we no longer benefit from the great `testifylint` linter for tests. diff --git a/embed.go b/embed.go index 1f42847..0d0b699 100644 --- a/embed.go +++ b/embed.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package spec import ( diff --git a/errors.go b/errors.go index 6992c7b..4623bc8 100644 --- a/errors.go +++ b/errors.go @@ -1,19 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package spec import "errors" // Error codes var ( - // ErrUnknownTypeForReference indicates that a resolved reference was found in an unsupported container type + // ErrUnknownTypeForReference indicates that a resolved reference was found in an unsupported container type. ErrUnknownTypeForReference = errors.New("unknown type for the resolved reference") - // ErrResolveRefNeedsAPointer indicates that a $ref target must be a valid JSON pointer + // ErrResolveRefNeedsAPointer indicates that a $ref target must be a valid JSON pointer. ErrResolveRefNeedsAPointer = errors.New("resolve ref: target needs to be a pointer") // ErrDerefUnsupportedType indicates that a resolved reference was found in an unsupported container type. - // At the moment, $ref are supported only inside: schemas, parameters, responses, path items + // At the moment, $ref are supported only inside: schemas, parameters, responses, path items. ErrDerefUnsupportedType = errors.New("deref: unsupported type") - // ErrExpandUnsupportedType indicates that $ref expansion is attempted on some invalid type + // ErrExpandUnsupportedType indicates that $ref expansion is attempted on some invalid type. ErrExpandUnsupportedType = errors.New("expand: unsupported type. Input should be of type *Parameter or *Response") + + // ErrSpec is an error raised by the spec package. + ErrSpec = errors.New("spec error") ) diff --git a/expander.go b/expander.go index dd7eb6e..de1cc4c 100644 --- a/expander.go +++ b/expander.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -19,6 +8,8 @@ import ( "fmt" ) +const smallPrealloc = 10 + // ExpandOptions provides options for the spec expander. // // RelativeBase is the path to the root document. This can be a remote URL or a path to a local file. @@ -56,7 +47,7 @@ func ExpandSpec(spec *Swagger, options *ExpandOptions) error { if !options.SkipSchemas { for key, definition := range spec.Definitions { - parentRefs := make([]string, 0, 10) + parentRefs := make([]string, 0, smallPrealloc) parentRefs = append(parentRefs, "#/definitions/"+key) def, err := expandSchema(definition, parentRefs, resolver, specBasePath) @@ -102,7 +93,7 @@ const rootBase = ".root" // baseForRoot loads in the cache the root document and produces a fake ".root" base path entry // for further $ref resolution -func baseForRoot(root interface{}, cache ResolutionCache) string { +func baseForRoot(root any, cache ResolutionCache) string { // cache the root document to resolve $ref's normalizedBase := normalizeBase(rootBase) @@ -114,7 +105,7 @@ func baseForRoot(root interface{}, cache ResolutionCache) string { return normalizedBase } - root = map[string]interface{}{} + root = map[string]any{} } cache.Set(normalizedBase, root) @@ -130,7 +121,7 @@ func baseForRoot(root interface{}, cache ResolutionCache) string { // (use ExpandSchemaWithBasePath to resolve external references). // // Setting the cache is optional and this parameter may safely be left to nil. -func ExpandSchema(schema *Schema, root interface{}, cache ResolutionCache) error { +func ExpandSchema(schema *Schema, root any, cache ResolutionCache) error { cache = cacheOrDefault(cache) if root == nil { root = schema @@ -160,7 +151,7 @@ func ExpandSchemaWithBasePath(schema *Schema, cache ResolutionCache, opts *Expan resolver := defaultSchemaLoader(nil, opts, cache, nil) - parentRefs := make([]string, 0, 10) + parentRefs := make([]string, 0, smallPrealloc) s, err := expandSchema(*schema, parentRefs, resolver, opts.RelativeBase) if err != nil { return err @@ -392,7 +383,7 @@ func expandPathItem(pathItem *PathItem, resolver *schemaLoader, basePath string) return nil } - parentRefs := make([]string, 0, 10) + parentRefs := make([]string, 0, smallPrealloc) if err := resolver.deref(pathItem, parentRefs, basePath); resolver.shouldStopOnError(err) { return err } @@ -467,7 +458,7 @@ func expandOperation(op *Operation, resolver *schemaLoader, basePath string) err // (use ExpandResponse to resolve external references). // // Setting the cache is optional and this parameter may safely be left to nil. -func ExpandResponseWithRoot(response *Response, root interface{}, cache ResolutionCache) error { +func ExpandResponseWithRoot(response *Response, root any, cache ResolutionCache) error { cache = cacheOrDefault(cache) opts := &ExpandOptions{ RelativeBase: baseForRoot(root, cache), @@ -493,7 +484,7 @@ func ExpandResponse(response *Response, basePath string) error { // // Notice that it is impossible to reference a json schema in a different document other than root // (use ExpandParameter to resolve external references). -func ExpandParameterWithRoot(parameter *Parameter, root interface{}, cache ResolutionCache) error { +func ExpandParameterWithRoot(parameter *Parameter, root any, cache ResolutionCache) error { cache = cacheOrDefault(cache) opts := &ExpandOptions{ @@ -516,7 +507,7 @@ func ExpandParameter(parameter *Parameter, basePath string) error { return expandParameterOrResponse(parameter, resolver, opts.RelativeBase) } -func getRefAndSchema(input interface{}) (*Ref, *Schema, error) { +func getRefAndSchema(input any) (*Ref, *Schema, error) { var ( ref *Ref sch *Schema @@ -542,7 +533,7 @@ func getRefAndSchema(input interface{}) (*Ref, *Schema, error) { return ref, sch, nil } -func expandParameterOrResponse(input interface{}, resolver *schemaLoader, basePath string) error { +func expandParameterOrResponse(input any, resolver *schemaLoader, basePath string) error { ref, sch, err := getRefAndSchema(input) if err != nil { return err @@ -552,7 +543,7 @@ func expandParameterOrResponse(input interface{}, resolver *schemaLoader, basePa return nil } - parentRefs := make([]string, 0, 10) + parentRefs := make([]string, 0, smallPrealloc) if ref != nil { // dereference this $ref if err = resolver.deref(input, parentRefs, basePath); resolver.shouldStopOnError(err) { @@ -560,6 +551,9 @@ func expandParameterOrResponse(input interface{}, resolver *schemaLoader, basePa } ref, sch, _ = getRefAndSchema(input) + if ref == nil { + ref = &Ref{} // empty ref + } } if ref.String() != "" { diff --git a/expander_test.go b/expander_test.go index 8b0c8c4..d0a8ef8 100644 --- a/expander_test.go +++ b/expander_test.go @@ -1,21 +1,12 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec import ( + "embed" "encoding/json" + "fmt" "io" "log" "net/http" @@ -24,8 +15,8 @@ import ( "path/filepath" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) const ( @@ -36,12 +27,43 @@ const ( extraRefFixture = "fixtures/expansion/extraRef.json" ) +//nolint:gochecknoglobals // it's okay to have embedded test fixtures as globals var ( + //go:embed fixtures/*/*.json fixtures/*/*.yaml fixtures/*/*.yml + fixtureAssets embed.FS + + // PetStore20 json doc for swagger 2.0 pet store. + PetStore20 []byte + // PetStoreJSONMessage json raw message for Petstore20 - PetStoreJSONMessage = json.RawMessage([]byte(PetStore20)) - specs = filepath.Join("fixtures", "specs") + PetStoreJSONMessage json.RawMessage + expectedExtraRef []byte + expectedPathItem []byte + + specs = filepath.Join("fixtures", "specs") ) +func init() { //nolint:gochecknoinits // it's okay to load embedded fixtures in init(). + // load embedded fixtures + + var err error + PetStore20, err = fixtureAssets.ReadFile("fixtures/expansion/petstore2.0.json") + if err != nil { + panic(fmt.Sprintf("could not find fixture: %v", err)) + } + PetStoreJSONMessage = json.RawMessage(PetStore20) + + expectedExtraRef, err = fixtureAssets.ReadFile("fixtures/expansion/expectedExtraRef.json") + if err != nil { + panic(fmt.Sprintf("could not find fixture: %v", err)) + } + + expectedPathItem, err = fixtureAssets.ReadFile("fixtures/expansion/expectedPathItem.json") + if err != nil { + panic(fmt.Sprintf("could not find fixture: %v", err)) + } +} + func TestExpand_Issue148(t *testing.T) { fp := filepath.Join("fixtures", "bugs", "schema-148.json") b, err := jsonDoc(fp) @@ -129,7 +151,6 @@ func TestExpand_EmptySpec(t *testing.T) { } func TestExpand_Spec(t *testing.T) { - // expansion of a rich spec specPath := filepath.Join("fixtures", "expansion", "all-the-things.json") specDoc, err := jsonDoc(specPath) @@ -819,7 +840,7 @@ func resolutionContextServer() *httptest.Server { if req.URL.Path == "/resolution.json" { b, _ := os.ReadFile(filepath.Join(specs, "resolution.json")) - var ctnt map[string]interface{} + var ctnt map[string]any _ = json.Unmarshal(b, &ctnt) ctnt["id"] = servedAt @@ -831,7 +852,7 @@ func resolutionContextServer() *httptest.Server { } if req.URL.Path == "/resolution2.json" { b, _ := os.ReadFile(filepath.Join(specs, "resolution2.json")) - var ctnt map[string]interface{} + var ctnt map[string]any _ = json.Unmarshal(b, &ctnt) ctnt["id"] = servedAt @@ -843,7 +864,7 @@ func resolutionContextServer() *httptest.Server { if req.URL.Path == "/boolProp.json" { rw.Header().Set("Content-Type", "application/json") - b, _ := json.Marshal(map[string]interface{}{ + b, _ := json.Marshal(map[string]any{ "type": "boolean", }) _, _ = rw.Write(b) @@ -852,7 +873,7 @@ func resolutionContextServer() *httptest.Server { if req.URL.Path == "/deeper/stringProp.json" { rw.Header().Set("Content-Type", "application/json") - b, _ := json.Marshal(map[string]interface{}{ + b, _ := json.Marshal(map[string]any{ "type": "string", }) _, _ = rw.Write(b) @@ -861,9 +882,9 @@ func resolutionContextServer() *httptest.Server { if req.URL.Path == "/deeper/arrayProp.json" { rw.Header().Set("Content-Type", "application/json") - b, _ := json.Marshal(map[string]interface{}{ + b, _ := json.Marshal(map[string]any{ "type": "array", - "items": map[string]interface{}{ + "items": map[string]any{ "type": "file", }, }) @@ -1010,94 +1031,12 @@ func expandRootWithID(t testing.TB, root *Swagger, testcase string) { func TestExpand_PathItem(t *testing.T) { jazon, _ := expandThisOrDieTrying(t, pathItemsFixture) - assert.JSONEq(t, `{ - "swagger": "2.0", - "info": { - "title": "PathItems refs", - "version": "1.0" - }, - "paths": { - "/todos": { - "get": { - "responses": { - "200": { - "description": "List Todos", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "404": { - "description": "error" - } - } - } - } - } - }`, jazon) + assert.JSONEq(t, string(expectedPathItem), jazon) } func TestExpand_ExtraItems(t *testing.T) { jazon, _ := expandThisOrDieTrying(t, extraRefFixture) - assert.JSONEq(t, `{ - "schemes": [ - "http" - ], - "swagger": "2.0", - "info": { - "title": "Supported, but non Swagger 20 compliant $ref constructs", - "version": "2.1.0" - }, - "host": "item.com", - "basePath": "/extraRefs", - "paths": { - "/employees": { - "get": { - "summary": "List Employee Types", - "operationId": "LIST-Employees", - "parameters": [ - { - "description": "unsupported $ref in simple param", - "type": "array", - "items": { - "$ref": "#/definitions/arrayType" - }, - "name": "myQueryParam", - "in": "query" - } - ], - "responses": { - "200": { - "description": "unsupported $ref in header", - "schema": { - "type": "string" - }, - "headers": { - "X-header": { - "type": "array", - "items": { - "$ref": "#/definitions/headerType" - } - } - } - } - } - } - } - }, - "definitions": { - "arrayType": { - "type": "integer", - "format": "int32" - }, - "headerType": { - "type": "string", - "format": "uuid" - } - } - }`, jazon) + assert.JSONEq(t, string(expectedExtraRef), jazon) } func TestExpand_Issue145(t *testing.T) { @@ -1113,7 +1052,7 @@ func TestExpand_Issue145(t *testing.T) { t.Run("empty root is cached", func(t *testing.T) { value, ok := cache.Get(pseudoRoot) require.True(t, ok) // found in cache - asMap, ok := value.(map[string]interface{}) + asMap, ok := value.(map[string]any) require.True(t, ok) require.Empty(t, asMap) }) @@ -1121,12 +1060,12 @@ func TestExpand_Issue145(t *testing.T) { t.Run("with non-nil root, empty cache", func(t *testing.T) { cache := defaultResolutionCache() - require.Equal(t, pseudoRoot, baseForRoot(map[string]interface{}{"key": "arbitrary"}, cache)) + require.Equal(t, pseudoRoot, baseForRoot(map[string]any{"key": "arbitrary"}, cache)) t.Run("non-empty root is cached", func(t *testing.T) { value, ok := cache.Get(pseudoRoot) require.True(t, ok) // found in cache - asMap, ok := value.(map[string]interface{}) + asMap, ok := value.(map[string]any) require.True(t, ok) require.Contains(t, asMap, "key") require.Equal(t, "arbitrary", asMap["key"]) @@ -1138,7 +1077,7 @@ func TestExpand_Issue145(t *testing.T) { t.Run("non-empty root is kept", func(t *testing.T) { value, ok := cache.Get(pseudoRoot) require.True(t, ok) // found in cache - asMap, ok := value.(map[string]interface{}) + asMap, ok := value.(map[string]any) require.True(t, ok) require.Contains(t, asMap, "key") require.Equal(t, "arbitrary", asMap["key"]) @@ -1146,286 +1085,3 @@ func TestExpand_Issue145(t *testing.T) { }) }) } - -// PetStore20 json doc for swagger 2.0 pet store -const PetStore20 = `{ - "swagger": "2.0", - "info": { - "version": "1.0.0", - "title": "Swagger Petstore", - "contact": { - "name": "Wordnik API Team", - "url": "http://developer.wordnik.com" - }, - "license": { - "name": "Creative Commons 4.0 International", - "url": "http://creativecommons.org/licenses/by/4.0/" - } - }, - "host": "petstore.swagger.wordnik.com", - "basePath": "/api", - "schemes": [ - "http" - ], - "paths": { - "/pets": { - "get": { - "security": [ - { - "basic": [] - } - ], - "tags": [ "Pet Operations" ], - "operationId": "getAllPets", - "parameters": [ - { - "name": "status", - "in": "query", - "description": "The status to filter by", - "type": "string" - }, - { - "name": "limit", - "in": "query", - "description": "The maximum number of results to return", - "type": "integer", - "format": "int64" - } - ], - "summary": "Finds all pets in the system", - "responses": { - "200": { - "description": "Pet response", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/Pet" - } - } - }, - "default": { - "description": "Unexpected error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "post": { - "security": [ - { - "basic": [] - } - ], - "tags": [ "Pet Operations" ], - "operationId": "createPet", - "summary": "Creates a new pet", - "consumes": ["application/x-yaml"], - "produces": ["application/x-yaml"], - "parameters": [ - { - "name": "pet", - "in": "body", - "description": "The Pet to create", - "required": true, - "schema": { - "$ref": "#/definitions/newPet" - } - } - ], - "responses": { - "200": { - "description": "Created Pet response", - "schema": { - "$ref": "#/definitions/Pet" - } - }, - "default": { - "description": "Unexpected error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/pets/{id}": { - "delete": { - "security": [ - { - "apiKey": [] - } - ], - "description": "Deletes the Pet by id", - "operationId": "deletePet", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "ID of pet to delete", - "required": true, - "type": "integer", - "format": "int64" - } - ], - "responses": { - "204": { - "description": "pet deleted" - }, - "default": { - "description": "unexpected error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "get": { - "tags": [ "Pet Operations" ], - "operationId": "getPetById", - "summary": "Finds the pet by id", - "responses": { - "200": { - "description": "Pet response", - "schema": { - "$ref": "#/definitions/Pet" - } - }, - "default": { - "description": "Unexpected error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "parameters": [ - { - "name": "id", - "in": "path", - "description": "ID of pet", - "required": true, - "type": "integer", - "format": "int64" - } - ] - } - }, - "definitions": { - "Category": { - "id": "Category", - "properties": { - "id": { - "format": "int64", - "type": "integer" - }, - "name": { - "type": "string" - } - } - }, - "Pet": { - "id": "Pet", - "properties": { - "category": { - "$ref": "#/definitions/Category" - }, - "id": { - "description": "unique identifier for the pet", - "format": "int64", - "maximum": 100.0, - "minimum": 0.0, - "type": "integer" - }, - "name": { - "type": "string" - }, - "photoUrls": { - "items": { - "type": "string" - }, - "type": "array" - }, - "status": { - "description": "pet status in the store", - "enum": [ - "available", - "pending", - "sold" - ], - "type": "string" - }, - "tags": { - "items": { - "$ref": "#/definitions/Tag" - }, - "type": "array" - } - }, - "required": [ - "id", - "name" - ] - }, - "newPet": { - "anyOf": [ - { - "$ref": "#/definitions/Pet" - }, - { - "required": [ - "name" - ] - } - ] - }, - "Tag": { - "id": "Tag", - "properties": { - "id": { - "format": "int64", - "type": "integer" - }, - "name": { - "type": "string" - } - } - }, - "Error": { - "required": [ - "code", - "message" - ], - "properties": { - "code": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - } - } - } - }, - "consumes": [ - "application/json", - "application/xml" - ], - "produces": [ - "application/json", - "application/xml", - "text/plain", - "text/html" - ], - "securityDefinitions": { - "basic": { - "type": "basic" - }, - "apiKey": { - "type": "apiKey", - "in": "header", - "name": "X-API-KEY" - } - } -} -` diff --git a/external_docs.go b/external_docs.go index 88add91..17b8efb 100644 --- a/external_docs.go +++ b/external_docs.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec diff --git a/external_docs_test.go b/external_docs_test.go index 9184527..8f5d7cd 100644 --- a/external_docs_test.go +++ b/external_docs_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -19,7 +8,7 @@ import ( ) func TestIntegrationExternalDocs(t *testing.T) { - var extDocs = ExternalDocumentation{Description: "the name", URL: "the url"} + extDocs := ExternalDocumentation{Description: "the name", URL: "the url"} const extDocsYAML = "description: the name\nurl: the url\n" const extDocsJSON = `{"description":"the name","url":"the url"}` assertSerializeJSON(t, extDocs, extDocsJSON) diff --git a/fixtures/expansion/expectedExtraRef.json b/fixtures/expansion/expectedExtraRef.json new file mode 100644 index 0000000..24da091 --- /dev/null +++ b/fixtures/expansion/expectedExtraRef.json @@ -0,0 +1,57 @@ +{ + "schemes": [ + "http" + ], + "swagger": "2.0", + "info": { + "title": "Supported, but non Swagger 20 compliant $ref constructs", + "version": "2.1.0" + }, + "host": "item.com", + "basePath": "/extraRefs", + "paths": { + "/employees": { + "get": { + "summary": "List Employee Types", + "operationId": "LIST-Employees", + "parameters": [ + { + "description": "unsupported $ref in simple param", + "type": "array", + "items": { + "$ref": "#/definitions/arrayType" + }, + "name": "myQueryParam", + "in": "query" + } + ], + "responses": { + "200": { + "description": "unsupported $ref in header", + "schema": { + "type": "string" + }, + "headers": { + "X-header": { + "type": "array", + "items": { + "$ref": "#/definitions/headerType" + } + } + } + } + } + } + } + }, + "definitions": { + "arrayType": { + "type": "integer", + "format": "int32" + }, + "headerType": { + "type": "string", + "format": "uuid" + } + } +} diff --git a/fixtures/expansion/expectedPathItem.json b/fixtures/expansion/expectedPathItem.json new file mode 100644 index 0000000..6eed8b6 --- /dev/null +++ b/fixtures/expansion/expectedPathItem.json @@ -0,0 +1,27 @@ +{ + "swagger": "2.0", + "info": { + "title": "PathItems refs", + "version": "1.0" + }, + "paths": { + "/todos": { + "get": { + "responses": { + "200": { + "description": "List Todos", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "404": { + "description": "error" + } + } + } + } + } +} diff --git a/fixtures/expansion/petstore2.0.json b/fixtures/expansion/petstore2.0.json new file mode 100644 index 0000000..7fc9d88 --- /dev/null +++ b/fixtures/expansion/petstore2.0.json @@ -0,0 +1,280 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "contact": { + "name": "Wordnik API Team", + "url": "http://developer.wordnik.com" + }, + "license": { + "name": "Creative Commons 4.0 International", + "url": "http://creativecommons.org/licenses/by/4.0/" + } + }, + "host": "petstore.swagger.wordnik.com", + "basePath": "/api", + "schemes": [ + "http" + ], + "paths": { + "/pets": { + "get": { + "security": [ + { + "basic": [] + } + ], + "tags": [ "Pet Operations" ], + "operationId": "getAllPets", + "parameters": [ + { + "name": "status", + "in": "query", + "description": "The status to filter by", + "type": "string" + }, + { + "name": "limit", + "in": "query", + "description": "The maximum number of results to return", + "type": "integer", + "format": "int64" + } + ], + "summary": "Finds all pets in the system", + "responses": { + "200": { + "description": "Pet response", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "default": { + "description": "Unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "post": { + "security": [ + { + "basic": [] + } + ], + "tags": [ "Pet Operations" ], + "operationId": "createPet", + "summary": "Creates a new pet", + "consumes": ["application/x-yaml"], + "produces": ["application/x-yaml"], + "parameters": [ + { + "name": "pet", + "in": "body", + "description": "The Pet to create", + "required": true, + "schema": { + "$ref": "#/definitions/newPet" + } + } + ], + "responses": { + "200": { + "description": "Created Pet response", + "schema": { + "$ref": "#/definitions/Pet" + } + }, + "default": { + "description": "Unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/pets/{id}": { + "delete": { + "security": [ + { + "apiKey": [] + } + ], + "description": "Deletes the Pet by id", + "operationId": "deletePet", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of pet to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "204": { + "description": "pet deleted" + }, + "default": { + "description": "unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "get": { + "tags": [ "Pet Operations" ], + "operationId": "getPetById", + "summary": "Finds the pet by id", + "responses": { + "200": { + "description": "Pet response", + "schema": { + "$ref": "#/definitions/Pet" + } + }, + "default": { + "description": "Unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of pet", + "required": true, + "type": "integer", + "format": "int64" + } + ] + } + }, + "definitions": { + "Category": { + "id": "Category", + "properties": { + "id": { + "format": "int64", + "type": "integer" + }, + "name": { + "type": "string" + } + } + }, + "Pet": { + "id": "Pet", + "properties": { + "category": { + "$ref": "#/definitions/Category" + }, + "id": { + "description": "unique identifier for the pet", + "format": "int64", + "maximum": 100.0, + "minimum": 0.0, + "type": "integer" + }, + "name": { + "type": "string" + }, + "photoUrls": { + "items": { + "type": "string" + }, + "type": "array" + }, + "status": { + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ], + "type": "string" + }, + "tags": { + "items": { + "$ref": "#/definitions/Tag" + }, + "type": "array" + } + }, + "required": [ + "id", + "name" + ] + }, + "newPet": { + "anyOf": [ + { + "$ref": "#/definitions/Pet" + }, + { + "required": [ + "name" + ] + } + ] + }, + "Tag": { + "id": "Tag", + "properties": { + "id": { + "format": "int64", + "type": "integer" + }, + "name": { + "type": "string" + } + } + }, + "Error": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + }, + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/json", + "application/xml", + "text/plain", + "text/html" + ], + "securityDefinitions": { + "basic": { + "type": "basic" + }, + "apiKey": { + "type": "apiKey", + "in": "header", + "name": "X-API-KEY" + } + } +} diff --git a/fixtures/specs/mini_spec.json b/fixtures/specs/mini_spec.json new file mode 100644 index 0000000..e153eaf --- /dev/null +++ b/fixtures/specs/mini_spec.json @@ -0,0 +1,18 @@ +{ + "swagger": "2.0", + "info": { + "version": "0.0.0", + "title": "Simple API" + }, + "paths": { + "/": { + "get": { + "responses": { + "200": { + "description": "OK" + } + } + } + } + } +} diff --git a/fixtures/specs/minimal_spec.json b/fixtures/specs/minimal_spec.json new file mode 100644 index 0000000..c7c2f7f --- /dev/null +++ b/fixtures/specs/minimal_spec.json @@ -0,0 +1,44 @@ +{ + "swagger": "2.0", + "info": { + "version": "0.0.0", + "title": "Simple API" + }, + "securityDefinitions": { + "basic": { + "type": "basic" + }, + "apiKey": { + "type": "apiKey", + "in": "header", + "name": "X-API-KEY" + }, + "queryKey": { + "type": "apiKey", + "in": "query", + "name": "api_key" + } + }, + "paths": { + "/": { + "get": { + "security": [ + { + "apiKey": [], + "basic": [] + }, + {}, + { + "queryKey": [], + "basic": [] + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + } +} diff --git a/fixtures/specs/spec.json b/fixtures/specs/spec.json new file mode 100644 index 0000000..0b65bcc --- /dev/null +++ b/fixtures/specs/spec.json @@ -0,0 +1,46 @@ +{ + "id": "http://localhost:3849/api-docs", + "consumes": ["application/json", "application/x-yaml"], + "produces": ["application/json"], + "schemes": ["http", "https"], + "swagger": "2.0", + "info": { + "contact": { + "name": "wordnik api team", + "url": "http://developer.wordnik.com" + }, + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "license": { + "name": "Creative Commons 4.0 International", + "url": "http://creativecommons.org/licenses/by/4.0/" + }, + "termsOfService": "http://helloreverb.com/terms/", + "title": "Swagger Sample API", + "version": "1.0.9-abcd", + "x-framework": "go-swagger" + }, + "host": "some.api.out.there", + "basePath": "/", + "paths": {"x-framework":"go-swagger","/":{"$ref":"cats"}}, + "definitions": { "Category": { "type": "string"} }, + "parameters": { + "categoryParam": { + "name": "category", + "in": "query", + "type": "string" + } + }, + "responses": { "EmptyAnswer": { "description": "no data to return for this operation" } }, + "securityDefinitions": { + "internalApiKey": { + "type": "apiKey", + "in": "header", + "name": "api_key" + } + }, + "security": [{"internalApiKey":[]}], + "tags": [{"name":"pets"}], + "externalDocs": {"description":"the name","url":"the url"}, + "x-some-extension": "vendor", + "x-schemes": ["unix","amqp"] +} diff --git a/go.mod b/go.mod index 996f90a..b12107b 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,20 @@ module github.com/go-openapi/spec require ( - github.com/go-openapi/jsonpointer v0.21.0 - github.com/go-openapi/jsonreference v0.21.0 - github.com/go-openapi/swag v0.23.0 - github.com/stretchr/testify v1.9.0 - gopkg.in/yaml.v3 v3.0.1 + github.com/go-openapi/jsonpointer v0.22.4 + github.com/go-openapi/jsonreference v0.21.4 + github.com/go-openapi/swag/conv v0.25.4 + github.com/go-openapi/swag/jsonname v0.25.4 + github.com/go-openapi/swag/jsonutils v0.25.4 + github.com/go-openapi/swag/loading v0.25.4 + github.com/go-openapi/swag/stringutils v0.25.4 + github.com/go-openapi/testify/v2 v2.0.2 + go.yaml.in/yaml/v3 v3.0.4 ) require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/go-openapi/swag/typeutils v0.25.4 // indirect + github.com/go-openapi/swag/yamlutils v0.25.4 // indirect ) -go 1.20 +go 1.25.5 diff --git a/go.sum b/go.sum index b5bc1d1..bcc4745 100644 --- a/go.sum +++ b/go.sum @@ -1,23 +1,28 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= -github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= -github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= +github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= +github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8= +github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4= +github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= +github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= +github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= +github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= +github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= +github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM= +github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= +github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= +github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= +github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= +github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= +github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= +github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= +github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= +github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= +github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/header.go b/header.go index 9dfd17b..ab251ef 100644 --- a/header.go +++ b/header.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -19,7 +8,7 @@ import ( "strings" "github.com/go-openapi/jsonpointer" - "github.com/go-openapi/swag" + "github.com/go-openapi/swag/jsonutils" ) const ( @@ -68,20 +57,20 @@ func (h *Header) CollectionOf(items *Items, format string) *Header { } // WithDefault sets the default value on this item -func (h *Header) WithDefault(defaultValue interface{}) *Header { +func (h *Header) WithDefault(defaultValue any) *Header { h.Default = defaultValue return h } // WithMaxLength sets a max length value -func (h *Header) WithMaxLength(max int64) *Header { - h.MaxLength = &max +func (h *Header) WithMaxLength(maximum int64) *Header { + h.MaxLength = &maximum return h } // WithMinLength sets a min length value -func (h *Header) WithMinLength(min int64) *Header { - h.MinLength = &min +func (h *Header) WithMinLength(minimum int64) *Header { + h.MinLength = &minimum return h } @@ -98,22 +87,22 @@ func (h *Header) WithMultipleOf(number float64) *Header { } // WithMaximum sets a maximum number value -func (h *Header) WithMaximum(max float64, exclusive bool) *Header { - h.Maximum = &max +func (h *Header) WithMaximum(maximum float64, exclusive bool) *Header { + h.Maximum = &maximum h.ExclusiveMaximum = exclusive return h } // WithMinimum sets a minimum number value -func (h *Header) WithMinimum(min float64, exclusive bool) *Header { - h.Minimum = &min +func (h *Header) WithMinimum(minimum float64, exclusive bool) *Header { + h.Minimum = &minimum h.ExclusiveMinimum = exclusive return h } // WithEnum sets a the enum values (replace) -func (h *Header) WithEnum(values ...interface{}) *Header { - h.Enum = append([]interface{}{}, values...) +func (h *Header) WithEnum(values ...any) *Header { + h.Enum = append([]any{}, values...) return h } @@ -161,7 +150,7 @@ func (h Header) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return swag.ConcatJSON(b1, b2, b3), nil + return jsonutils.ConcatJSON(b1, b2, b3), nil } // UnmarshalJSON unmarshals this header from JSON @@ -179,7 +168,7 @@ func (h *Header) UnmarshalJSON(data []byte) error { } // JSONLookup look up a value by the json property name -func (h Header) JSONLookup(token string) (interface{}, error) { +func (h Header) JSONLookup(token string) (any, error) { if ex, ok := h.Extensions[token]; ok { return &ex, nil } diff --git a/header_test.go b/header_test.go index 67a893a..6d642ee 100644 --- a/header_test.go +++ b/header_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -18,9 +7,9 @@ import ( "encoding/json" "testing" - "github.com/go-openapi/swag" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/swag/conv" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) const epsilon = 1e-9 @@ -28,12 +17,13 @@ const epsilon = 1e-9 func float64Ptr(f float64) *float64 { return &f } + func int64Ptr(f int64) *int64 { return &f } var header = Header{ - VendorExtensible: VendorExtensible{Extensions: map[string]interface{}{ + VendorExtensible: VendorExtensible{Extensions: map[string]any{ "x-framework": "swagger-go", }}, HeaderProps: HeaderProps{Description: "the description of this header"}, @@ -57,7 +47,7 @@ var header = Header{ MinItems: int64Ptr(5), UniqueItems: true, MultipleOf: float64Ptr(5), - Enum: []interface{}{"hello", "world"}, + Enum: []any{"hello", "world"}, }, } @@ -87,7 +77,7 @@ const headerJSON = `{ func TestIntegrationHeader(t *testing.T) { var actual Header require.NoError(t, json.Unmarshal([]byte(headerJSON), &actual)) - assert.EqualValues(t, actual, header) + assert.Equal(t, actual, header) assertParsesJSON(t, headerJSON, header) } @@ -104,13 +94,13 @@ func TestJSONLookupHeader(t *testing.T) { require.True(t, ok) assert.Equal(t, "8", def) - var x *interface{} + var x *any res, err = header.JSONLookup("x-framework") require.NoError(t, err) require.NotNil(t, res) require.IsType(t, x, res) - x, ok = res.(*interface{}) + x, ok = res.(*any) require.True(t, ok) assert.EqualValues(t, "swagger-go", *x) @@ -118,15 +108,15 @@ func TestJSONLookupHeader(t *testing.T) { require.Error(t, err) require.Nil(t, res) - var max *float64 + var maximum *float64 res, err = header.JSONLookup("maximum") require.NoError(t, err) require.NotNil(t, res) - require.IsType(t, max, res) + require.IsType(t, maximum, res) - max, ok = res.(*float64) + maximum, ok = res.(*float64) require.True(t, ok) - assert.InDelta(t, float64(100), *max, epsilon) + assert.InDelta(t, float64(100), *maximum, epsilon) } func TestResponseHeaueder(t *testing.T) { @@ -144,7 +134,7 @@ func TestWithHeader(t *testing.T) { i := new(Items).Typed("string", "date") h = new(Header).CollectionOf(i, "pipe") - assert.EqualValues(t, *i, *h.Items) + assert.Equal(t, *i, *h.Items) assert.Equal(t, "pipe", h.CollectionFormat) h = new(Header).WithDefault([]string{"a", "b", "c"}).WithMaxLength(10).WithMinLength(3) @@ -162,7 +152,7 @@ func TestWithHeader(t *testing.T) { h = new(Header).WithEnum("a", "b", "c") assert.Equal(t, Header{ CommonValidations: CommonValidations{ - Enum: []interface{}{ + Enum: []any{ "a", "b", "c", @@ -172,6 +162,6 @@ func TestWithHeader(t *testing.T) { } func TestHeaderWithValidation(t *testing.T) { - h := new(Header).WithValidations(CommonValidations{MaxLength: swag.Int64(15)}) - assert.EqualValues(t, swag.Int64(15), h.MaxLength) + h := new(Header).WithValidations(CommonValidations{MaxLength: conv.Pointer(int64(15))}) + assert.Equal(t, conv.Pointer(int64(15)), h.MaxLength) } diff --git a/helpers_spec_test.go b/helpers_spec_test.go index 4dd12c5..1bfa45e 100644 --- a/helpers_spec_test.go +++ b/helpers_spec_test.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package spec_test import ( @@ -8,9 +11,9 @@ import ( "testing" "github.com/go-openapi/spec" - "github.com/go-openapi/swag" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/swag/loading" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) var ( @@ -21,10 +24,10 @@ var ( func init() { // mimics what the go-openapi/load does testLoader = func(path string) (json.RawMessage, error) { - if swag.YAMLMatcher(path) { - return swag.YAMLDoc(path) + if loading.YAMLMatcher(path) { + return loading.YAMLDoc(path) } - data, err := swag.LoadFromFileOrHTTP(path) + data, err := loading.LoadFromFileOrHTTP(path) if err != nil { return nil, err } @@ -71,7 +74,8 @@ func assertRefInJSONRegexp(t testing.TB, jazon, match string) { // assertRefExpand ensures that all $ref in some json doc expand properly against a root document. // // "exclude" is a regexp pattern to ignore certain $ref (e.g. some specs may embed $ref that are not processed, such as extensions). -func assertRefExpand(t *testing.T, jazon, _ string, root interface{}, opts ...*spec.ExpandOptions) { +func assertRefExpand(t *testing.T, jazon, _ string, root any, opts ...*spec.ExpandOptions) { + t.Helper() if len(opts) > 0 { assertRefWithFunc(t, "expand-with-base", jazon, "", func(t *testing.T, match string) { ref := spec.RefSchema(match) @@ -87,7 +91,8 @@ func assertRefExpand(t *testing.T, jazon, _ string, root interface{}, opts ...*s }) } -func assertRefResolve(t *testing.T, jazon, exclude string, root interface{}, opts ...*spec.ExpandOptions) { +func assertRefResolve(t *testing.T, jazon, exclude string, root any, opts ...*spec.ExpandOptions) { + t.Helper() assertRefWithFunc(t, "resolve", jazon, exclude, func(t *testing.T, match string) { ref := spec.MustCreateRef(match) var ( @@ -110,6 +115,7 @@ func assertRefResolve(t *testing.T, jazon, exclude string, root interface{}, opt // // "exclude" is a regexp pattern to ignore certain $ref (e.g. some specs may embed $ref that are not processed, such as extensions). func assertRefWithFunc(t *testing.T, name, jazon, exclude string, asserter func(*testing.T, string)) { + t.Helper() filterRex := regexp.MustCompile(exclude) m := rex.FindAllStringSubmatch(jazon, -1) require.NotNil(t, m) @@ -136,15 +142,17 @@ func assertRefWithFunc(t *testing.T, name, jazon, exclude string, asserter func( } } -func asJSON(t testing.TB, sp interface{}) string { +func asJSON(tb testing.TB, sp any) string { + tb.Helper() bbb, err := json.MarshalIndent(sp, "", " ") - require.NoError(t, err) + require.NoError(tb, err) return string(bbb) } // assertNoRef ensures that no $ref is remaining in json doc func assertNoRef(t testing.TB, jazon string) { + t.Helper() m := rex.FindAllStringSubmatch(jazon, -1) require.Nil(t, m) } diff --git a/helpers_test.go b/helpers_test.go index f16a4dd..4226c1b 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package spec import ( @@ -7,15 +10,15 @@ import ( "strings" "testing" - "github.com/go-openapi/swag" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/swag/loading" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) var rex = regexp.MustCompile(`"\$ref":\s*"(.*?)"`) func jsonDoc(path string) (json.RawMessage, error) { - data, err := swag.LoadFromFileOrHTTP(path) + data, err := loading.LoadFromFileOrHTTP(path) if err != nil { return nil, err } @@ -105,7 +108,8 @@ func assertNoRef(t testing.TB, jazon string) { // assertRefExpand ensures that all $ref in some json doc expand properly against a root document. // // "exclude" is a regexp pattern to ignore certain $ref (e.g. some specs may embed $ref that are not processed, such as extensions). -func assertRefExpand(t *testing.T, jazon, _ string, root interface{}, opts ...*ExpandOptions) { +func assertRefExpand(t *testing.T, jazon, _ string, root any, opts ...*ExpandOptions) { + t.Helper() assertRefWithFunc(t, jazon, "", func(t *testing.T, match string) { ref := RefSchema(match) if len(opts) > 0 { @@ -120,7 +124,8 @@ func assertRefExpand(t *testing.T, jazon, _ string, root interface{}, opts ...*E // assertRefResolve ensures that all $ref in some json doc resolve properly against a root document. // // "exclude" is a regexp pattern to ignore certain $ref (e.g. some specs may embed $ref that are not processed, such as extensions). -func assertRefResolve(t *testing.T, jazon, exclude string, root interface{}, opts ...*ExpandOptions) { +func assertRefResolve(t *testing.T, jazon, exclude string, root any, opts ...*ExpandOptions) { + t.Helper() assertRefWithFunc(t, jazon, exclude, func(t *testing.T, match string) { ref := MustCreateRef(match) var ( @@ -143,6 +148,7 @@ func assertRefResolve(t *testing.T, jazon, exclude string, root interface{}, opt // // "exclude" is a regexp pattern to ignore certain $ref (e.g. some specs may embed $ref that are not processed, such as extensions). func assertRefWithFunc(t *testing.T, jazon, exclude string, asserter func(t *testing.T, match string)) { + t.Helper() filterRex := regexp.MustCompile(exclude) m := rex.FindAllStringSubmatch(jazon, -1) require.NotNil(t, m) @@ -165,9 +171,10 @@ func assertRefWithFunc(t *testing.T, jazon, exclude string, asserter func(t *tes } } -func asJSON(t testing.TB, sp interface{}) string { +func asJSON(tb testing.TB, sp any) string { + tb.Helper() bbb, err := json.MarshalIndent(sp, "", " ") - require.NoError(t, err) + require.NoError(tb, err) return string(bbb) } diff --git a/info.go b/info.go index 582f0fd..9401065 100644 --- a/info.go +++ b/info.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -20,14 +9,14 @@ import ( "strings" "github.com/go-openapi/jsonpointer" - "github.com/go-openapi/swag" + "github.com/go-openapi/swag/jsonutils" ) // Extensions vendor specific extensions -type Extensions map[string]interface{} +type Extensions map[string]any // Add adds a value to these extensions -func (e Extensions) Add(key string, value interface{}) { +func (e Extensions) Add(key string, value any) { realKey := strings.ToLower(key) e[realKey] = value } @@ -71,7 +60,7 @@ func (e Extensions) GetBool(key string) (bool, bool) { // GetStringSlice gets a string value from the extensions func (e Extensions) GetStringSlice(key string) ([]string, bool) { if v, ok := e[strings.ToLower(key)]; ok { - arr, isSlice := v.([]interface{}) + arr, isSlice := v.([]any) if !isSlice { return nil, false } @@ -94,19 +83,19 @@ type VendorExtensible struct { } // AddExtension adds an extension to this extensible object -func (v *VendorExtensible) AddExtension(key string, value interface{}) { +func (v *VendorExtensible) AddExtension(key string, value any) { if value == nil { return } if v.Extensions == nil { - v.Extensions = make(map[string]interface{}) + v.Extensions = make(map[string]any) } v.Extensions.Add(key, value) } // MarshalJSON marshals the extensions to json func (v VendorExtensible) MarshalJSON() ([]byte, error) { - toser := make(map[string]interface{}) + toser := make(map[string]any) for k, v := range v.Extensions { lk := strings.ToLower(k) if strings.HasPrefix(lk, "x-") { @@ -118,7 +107,7 @@ func (v VendorExtensible) MarshalJSON() ([]byte, error) { // UnmarshalJSON for this extensible object func (v *VendorExtensible) UnmarshalJSON(data []byte) error { - var d map[string]interface{} + var d map[string]any if err := json.Unmarshal(data, &d); err != nil { return err } @@ -126,7 +115,7 @@ func (v *VendorExtensible) UnmarshalJSON(data []byte) error { lk := strings.ToLower(k) if strings.HasPrefix(lk, "x-") { if v.Extensions == nil { - v.Extensions = map[string]interface{}{} + v.Extensions = map[string]any{} } v.Extensions[k] = vv } @@ -154,7 +143,7 @@ type Info struct { } // JSONLookup look up a value by the json property name -func (i Info) JSONLookup(token string) (interface{}, error) { +func (i Info) JSONLookup(token string) (any, error) { if ex, ok := i.Extensions[token]; ok { return &ex, nil } @@ -172,7 +161,7 @@ func (i Info) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return swag.ConcatJSON(b1, b2), nil + return jsonutils.ConcatJSON(b1, b2), nil } // UnmarshalJSON marshal this from JSON diff --git a/info_test.go b/info_test.go index a8e3fbe..b401861 100644 --- a/info_test.go +++ b/info_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -18,8 +7,8 @@ import ( "encoding/json" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) const infoJSON = `{ @@ -39,7 +28,7 @@ const infoJSON = `{ "x-framework": "go-swagger" }` -var info = Info{ +var testInfo = Info{ //nolint:gochecknoglobals InfoProps: InfoProps{ Version: "1.0.9-abcd", Title: "Swagger Sample API", @@ -47,30 +36,33 @@ var info = Info{ "the swagger-2.0 specification", TermsOfService: "http://helloreverb.com/terms/", Contact: &ContactInfo{ContactInfoProps: ContactInfoProps{Name: "wordnik api team", URL: "http://developer.wordnik.com"}}, - License: &License{LicenseProps: LicenseProps{ - Name: "Creative Commons 4.0 International", - URL: "http://creativecommons.org/licenses/by/4.0/", - }, + License: &License{ + LicenseProps: LicenseProps{ + Name: "Creative Commons 4.0 International", + URL: "http://creativecommons.org/licenses/by/4.0/", + }, }, }, - VendorExtensible: VendorExtensible{Extensions: map[string]interface{}{"x-framework": "go-swagger"}}, + VendorExtensible: VendorExtensible{Extensions: map[string]any{"x-framework": "go-swagger"}}, } -func TestIntegrationInfo_Serialize(t *testing.T) { - b, err := json.MarshalIndent(info, "", "\t") - require.NoError(t, err) - assert.Equal(t, infoJSON, string(b)) -} +func TestInfo(t *testing.T) { + t.Run("should marshal Info", func(t *testing.T) { + b, err := json.MarshalIndent(testInfo, "", "\t") + require.NoError(t, err) + assert.JSONEq(t, infoJSON, string(b)) + }) -func TestIntegrationInfo_Deserialize(t *testing.T) { - actual := Info{} - require.NoError(t, json.Unmarshal([]byte(infoJSON), &actual)) - assert.EqualValues(t, info, actual) -} + t.Run("should unmarshal Info", func(t *testing.T) { + actual := Info{} + require.NoError(t, json.Unmarshal([]byte(infoJSON), &actual)) + assert.Equal(t, testInfo, actual) + }) -func TestInfoGobEncoding(t *testing.T) { - var src, dst Info - require.NoError(t, json.Unmarshal([]byte(infoJSON), &src)) - assert.EqualValues(t, src, info) - doTestAnyGobEncoding(t, &src, &dst) + t.Run("should GobEncode Info", func(t *testing.T) { + var src, dst Info + require.NoError(t, json.Unmarshal([]byte(infoJSON), &src)) + assert.Equal(t, src, testInfo) + doTestAnyGobEncoding(t, &src, &dst) + }) } diff --git a/items.go b/items.go index e2afb21..d30ca35 100644 --- a/items.go +++ b/items.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -19,7 +8,7 @@ import ( "strings" "github.com/go-openapi/jsonpointer" - "github.com/go-openapi/swag" + "github.com/go-openapi/swag/jsonutils" ) const ( @@ -28,13 +17,13 @@ const ( // SimpleSchema describe swagger simple schemas for parameters and headers type SimpleSchema struct { - Type string `json:"type,omitempty"` - Nullable bool `json:"nullable,omitempty"` - Format string `json:"format,omitempty"` - Items *Items `json:"items,omitempty"` - CollectionFormat string `json:"collectionFormat,omitempty"` - Default interface{} `json:"default,omitempty"` - Example interface{} `json:"example,omitempty"` + Type string `json:"type,omitempty"` + Nullable bool `json:"nullable,omitempty"` + Format string `json:"format,omitempty"` + Items *Items `json:"items,omitempty"` + CollectionFormat string `json:"collectionFormat,omitempty"` + Default any `json:"default,omitempty"` + Example any `json:"example,omitempty"` } // TypeName return the type (or format) of a simple schema @@ -91,20 +80,20 @@ func (i *Items) CollectionOf(items *Items, format string) *Items { } // WithDefault sets the default value on this item -func (i *Items) WithDefault(defaultValue interface{}) *Items { +func (i *Items) WithDefault(defaultValue any) *Items { i.Default = defaultValue return i } // WithMaxLength sets a max length value -func (i *Items) WithMaxLength(max int64) *Items { - i.MaxLength = &max +func (i *Items) WithMaxLength(maximum int64) *Items { + i.MaxLength = &maximum return i } // WithMinLength sets a min length value -func (i *Items) WithMinLength(min int64) *Items { - i.MinLength = &min +func (i *Items) WithMinLength(minimum int64) *Items { + i.MinLength = &minimum return i } @@ -121,22 +110,22 @@ func (i *Items) WithMultipleOf(number float64) *Items { } // WithMaximum sets a maximum number value -func (i *Items) WithMaximum(max float64, exclusive bool) *Items { - i.Maximum = &max +func (i *Items) WithMaximum(maximum float64, exclusive bool) *Items { + i.Maximum = &maximum i.ExclusiveMaximum = exclusive return i } // WithMinimum sets a minimum number value -func (i *Items) WithMinimum(min float64, exclusive bool) *Items { - i.Minimum = &min +func (i *Items) WithMinimum(minimum float64, exclusive bool) *Items { + i.Minimum = &minimum i.ExclusiveMinimum = exclusive return i } // WithEnum sets a the enum values (replace) -func (i *Items) WithEnum(values ...interface{}) *Items { - i.Enum = append([]interface{}{}, values...) +func (i *Items) WithEnum(values ...any) *Items { + i.Enum = append([]any{}, values...) return i } @@ -213,11 +202,11 @@ func (i Items) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return swag.ConcatJSON(b4, b3, b1, b2), nil + return jsonutils.ConcatJSON(b4, b3, b1, b2), nil } // JSONLookup look up a value by the json property name -func (i Items) JSONLookup(token string) (interface{}, error) { +func (i Items) JSONLookup(token string) (any, error) { if token == jsonRef { return &i.Ref, nil } diff --git a/items_test.go b/items_test.go index 4d4e458..7e3f089 100644 --- a/items_test.go +++ b/items_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -18,37 +7,12 @@ import ( "encoding/json" "testing" - "github.com/go-openapi/swag" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/swag/conv" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) -var items = Items{ - Refable: Refable{Ref: MustCreateRef("Dog")}, - CommonValidations: CommonValidations{ - Maximum: float64Ptr(100), - ExclusiveMaximum: true, - ExclusiveMinimum: true, - Minimum: float64Ptr(5), - MaxLength: int64Ptr(100), - MinLength: int64Ptr(5), - Pattern: "\\w{1,5}\\w+", - MaxItems: int64Ptr(100), - MinItems: int64Ptr(5), - UniqueItems: true, - MultipleOf: float64Ptr(5), - Enum: []interface{}{"hello", "world"}, - }, - SimpleSchema: SimpleSchema{ - Type: "string", - Format: "date", - Items: &Items{ - Refable: Refable{Ref: MustCreateRef("Cat")}, - }, - CollectionFormat: "csv", - Default: "8", - }, -} +// testItems is now defined inside the test function to avoid global variable. const itemsJSON = `{ "items": { @@ -74,19 +38,71 @@ const itemsJSON = `{ }` func TestIntegrationItems(t *testing.T) { + testItems := Items{ + Refable: Refable{Ref: MustCreateRef("Dog")}, + CommonValidations: CommonValidations{ + Maximum: float64Ptr(100), + ExclusiveMaximum: true, + ExclusiveMinimum: true, + Minimum: float64Ptr(5), + MaxLength: int64Ptr(100), + MinLength: int64Ptr(5), + Pattern: "\\w{1,5}\\w+", + MaxItems: int64Ptr(100), + MinItems: int64Ptr(5), + UniqueItems: true, + MultipleOf: float64Ptr(5), + Enum: []any{"hello", "world"}, + }, + SimpleSchema: SimpleSchema{ + Type: "string", + Format: "date", + Items: &Items{ + Refable: Refable{Ref: MustCreateRef("Cat")}, + }, + CollectionFormat: "csv", + Default: "8", + }, + } var actual Items require.NoError(t, json.Unmarshal([]byte(itemsJSON), &actual)) - assert.EqualValues(t, actual, items) + assert.Equal(t, actual, testItems) - assertParsesJSON(t, itemsJSON, items) + assertParsesJSON(t, itemsJSON, testItems) } func TestTypeNameItems(t *testing.T) { var nilItems Items - assert.Equal(t, "", nilItems.TypeName()) - - assert.Equal(t, "date", items.TypeName()) - assert.Equal(t, "", items.ItemsTypeName()) + assert.Empty(t, nilItems.TypeName()) + + testItems := Items{ + Refable: Refable{Ref: MustCreateRef("Dog")}, + CommonValidations: CommonValidations{ + Maximum: float64Ptr(100), + ExclusiveMaximum: true, + ExclusiveMinimum: true, + Minimum: float64Ptr(5), + MaxLength: int64Ptr(100), + MinLength: int64Ptr(5), + Pattern: "\\w{1,5}\\w+", + MaxItems: int64Ptr(100), + MinItems: int64Ptr(5), + UniqueItems: true, + MultipleOf: float64Ptr(5), + Enum: []any{"hello", "world"}, + }, + SimpleSchema: SimpleSchema{ + Type: "string", + Format: "date", + Items: &Items{ + Refable: Refable{Ref: MustCreateRef("Cat")}, + }, + CollectionFormat: "csv", + Default: "8", + }, + } + assert.Equal(t, "date", testItems.TypeName()) + assert.Empty(t, testItems.ItemsTypeName()) nested := Items{ SimpleSchema: SimpleSchema{ @@ -110,7 +126,7 @@ func TestTypeNameItems(t *testing.T) { } assert.Equal(t, "string", simple.TypeName()) - assert.Equal(t, "", simple.ItemsTypeName()) + assert.Empty(t, simple.ItemsTypeName()) simple.Items = NewItems() simple.Type = "array" @@ -141,9 +157,9 @@ func TestItemsBuilder(t *testing.T) { Default: []string{"default-value"}, }, CommonValidations: CommonValidations{ - Enum: []interface{}{[]string{"abc", "efg"}, []string{"hij"}}, - MinItems: swag.Int64(1), - MaxItems: swag.Int64(4), + Enum: []any{[]string{"abc", "efg"}, []string{"hij"}}, + MinItems: conv.Pointer(int64(1)), + MaxItems: conv.Pointer(int64(4)), UniqueItems: true, }, }, @@ -151,42 +167,76 @@ func TestItemsBuilder(t *testing.T) { } func TestJSONLookupItems(t *testing.T) { - res, err := items.JSONLookup("$ref") - require.NoError(t, err) - require.NotNil(t, res) - require.IsType(t, &Ref{}, res) - - var ok bool - ref, ok := res.(*Ref) - require.True(t, ok) - assert.EqualValues(t, MustCreateRef("Dog"), *ref) - - var max *float64 - res, err = items.JSONLookup("maximum") - require.NoError(t, err) - require.NotNil(t, res) - require.IsType(t, max, res) - - max, ok = res.(*float64) - require.True(t, ok) - assert.InDelta(t, float64(100), *max, epsilon) - - var f string - res, err = items.JSONLookup("collectionFormat") - require.NoError(t, err) - require.NotNil(t, res) - require.IsType(t, f, res) - - f, ok = res.(string) - require.True(t, ok) - assert.Equal(t, "csv", f) - - res, err = items.JSONLookup("unknown") - require.Error(t, err) - require.Nil(t, res) + testItems := Items{ + Refable: Refable{Ref: MustCreateRef("Dog")}, + CommonValidations: CommonValidations{ + Maximum: float64Ptr(100), + ExclusiveMaximum: true, + ExclusiveMinimum: true, + Minimum: float64Ptr(5), + MaxLength: int64Ptr(100), + MinLength: int64Ptr(5), + Pattern: "\\w{1,5}\\w+", + MaxItems: int64Ptr(100), + MinItems: int64Ptr(5), + UniqueItems: true, + MultipleOf: float64Ptr(5), + Enum: []any{"hello", "world"}, + }, + SimpleSchema: SimpleSchema{ + Type: "string", + Format: "date", + Items: &Items{ + Refable: Refable{Ref: MustCreateRef("Cat")}, + }, + CollectionFormat: "csv", + Default: "8", + }, + } + t.Run(`lookup should find "$ref"`, func(t *testing.T) { + res, err := testItems.JSONLookup("$ref") + require.NoError(t, err) + require.NotNil(t, res) + require.IsType(t, &Ref{}, res) + + ref, ok := res.(*Ref) + require.True(t, ok) + assert.Equal(t, MustCreateRef("Dog"), *ref) + }) + + t.Run(`lookup should find "maximum"`, func(t *testing.T) { + var maximum *float64 + res, err := testItems.JSONLookup("maximum") + require.NoError(t, err) + require.NotNil(t, res) + require.IsType(t, maximum, res) + + var ok bool + maximum, ok = res.(*float64) + require.True(t, ok) + assert.InDelta(t, float64(100), *maximum, epsilon) + }) + + t.Run(`lookup should find "collectionFormat"`, func(t *testing.T) { + var f string + res, err := testItems.JSONLookup("collectionFormat") + require.NoError(t, err) + require.NotNil(t, res) + require.IsType(t, f, res) + + f, ok := res.(string) + require.True(t, ok) + assert.Equal(t, "csv", f) + }) + + t.Run(`lookup should fail on "unknown"`, func(t *testing.T) { + res, err := testItems.JSONLookup("unknown") + require.Error(t, err) + require.Nil(t, res) + }) } func TestItemsWithValidation(t *testing.T) { - i := new(Items).WithValidations(CommonValidations{MaxLength: swag.Int64(15)}) - assert.EqualValues(t, swag.Int64(15), i.MaxLength) + i := new(Items).WithValidations(CommonValidations{MaxLength: conv.Pointer(int64(15))}) + assert.Equal(t, conv.Pointer(int64(15)), i.MaxLength) } diff --git a/license.go b/license.go index b42f803..286b237 100644 --- a/license.go +++ b/license.go @@ -1,23 +1,12 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec import ( "encoding/json" - "github.com/go-openapi/swag" + "github.com/go-openapi/swag/jsonutils" ) // License information for the exposed API. @@ -52,5 +41,5 @@ func (l License) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return swag.ConcatJSON(b1, b2), nil + return jsonutils.ConcatJSON(b1, b2), nil } diff --git a/license_test.go b/license_test.go index 398f0d8..cdc3bce 100644 --- a/license_test.go +++ b/license_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -18,30 +7,34 @@ import ( "encoding/json" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) -var license = License{ - LicenseProps: LicenseProps{Name: "the name", URL: "the url"}, - VendorExtensible: VendorExtensible{Extensions: map[string]interface{}{"x-license": "custom term"}}} - -const licenseJSON = `{ +func TestIntegrationLicense(t *testing.T) { + const licenseJSON = `{ "name": "the name", "url": "the url", "x-license": "custom term" }` -func TestIntegrationLicense(t *testing.T) { + testLicense := License{ + LicenseProps: LicenseProps{Name: "the name", URL: "the url"}, + VendorExtensible: VendorExtensible{Extensions: map[string]any{"x-license": "custom term"}}, + } // const licenseYAML = "name: the name\nurl: the url\n" - b, err := json.MarshalIndent(license, "", "\t") - require.NoError(t, err) - assert.Equal(t, licenseJSON, string(b)) - - actual := License{} - err = json.Unmarshal([]byte(licenseJSON), &actual) - require.NoError(t, err) - assert.EqualValues(t, license, actual) + t.Run("should marshal license", func(t *testing.T) { + b, err := json.MarshalIndent(testLicense, "", "\t") + require.NoError(t, err) + assert.JSONEq(t, licenseJSON, string(b)) + }) + + t.Run("should unmarshal empty license", func(t *testing.T) { + actual := License{} + err := json.Unmarshal([]byte(licenseJSON), &actual) + require.NoError(t, err) + assert.Equal(t, testLicense, actual) + }) } diff --git a/normalizer.go b/normalizer.go index e8b6009..e1d7c58 100644 --- a/normalizer.go +++ b/normalizer.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -95,7 +84,7 @@ func denormalizeRef(ref *Ref, originalRelativeBase, id string) Ref { if id != "" { idBaseURL, err := parseURL(id) if err == nil { // if the schema id is not usable as a URI, ignore it - if ref, ok := rebase(ref, idBaseURL, true); ok { // rebase, but keep references to root unchaged (do not want $ref: "") + if ref, ok := rebase(ref, idBaseURL, true); ok { // rebase, but keep references to root unchanged (do not want $ref: "") // $ref relative to the ID of the schema in the root document return ref } @@ -129,8 +118,8 @@ func rebase(ref *Ref, v *url.URL, notEqual bool) (Ref, bool) { newBase.Fragment = u.Fragment - if strings.HasPrefix(u.Path, docPath) { - newBase.Path = strings.TrimPrefix(u.Path, docPath) + if after, ok := strings.CutPrefix(u.Path, docPath); ok { + newBase.Path = after } else { newBase.Path = strings.TrimPrefix(u.Path, v.Path) } diff --git a/normalizer_nonwindows.go b/normalizer_nonwindows.go index f19f1a8..0d55632 100644 --- a/normalizer_nonwindows.go +++ b/normalizer_nonwindows.go @@ -1,19 +1,7 @@ //go:build !windows -// +build !windows -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec diff --git a/normalizer_test.go b/normalizer_test.go index 8d72c28..48ab6bc 100644 --- a/normalizer_test.go +++ b/normalizer_test.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package spec import ( @@ -8,8 +11,8 @@ import ( "strings" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) const windowsOS = "windows" diff --git a/normalizer_windows.go b/normalizer_windows.go index a66c532..61515c9 100644 --- a/normalizer_windows.go +++ b/normalizer_windows.go @@ -1,18 +1,7 @@ // -build windows -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec diff --git a/operation.go b/operation.go index a69cca8..974f68a 100644 --- a/operation.go +++ b/operation.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -21,12 +10,12 @@ import ( "sort" "github.com/go-openapi/jsonpointer" - "github.com/go-openapi/swag" + "github.com/go-openapi/swag/jsonutils" ) func init() { - gob.Register(map[string]interface{}{}) - gob.Register([]interface{}{}) + gob.Register(map[string]any{}) + gob.Register([]any{}) } // OperationProps describes an operation @@ -49,28 +38,31 @@ type OperationProps struct { Responses *Responses `json:"responses,omitempty"` } -// MarshalJSON takes care of serializing operation properties to JSON +// MarshalJSON takes care of serializing operation properties to JSON. // // We use a custom marhaller here to handle a special cases related to -// the Security field. We need to preserve zero length slice +// the Security field. We need to preserve zero length slice. // while omitting the field when the value is nil/unset. func (op OperationProps) MarshalJSON() ([]byte, error) { type Alias OperationProps if op.Security == nil { return json.Marshal(&struct { - Security []map[string][]string `json:"security,omitempty"` *Alias + + Security []map[string][]string `json:"security,omitempty"` }{ - Security: op.Security, Alias: (*Alias)(&op), + Security: op.Security, }) } + return json.Marshal(&struct { - Security []map[string][]string `json:"security"` *Alias + + Security []map[string][]string `json:"security"` }{ - Security: op.Security, Alias: (*Alias)(&op), + Security: op.Security, }) } @@ -82,6 +74,14 @@ type Operation struct { OperationProps } +// NewOperation creates a new operation instance. +// It expects an ID as parameter but not passing an ID is also valid. +func NewOperation(id string) *Operation { + op := new(Operation) + op.ID = id + return op +} + // SuccessResponse gets a success response model func (o *Operation) SuccessResponse() (*Response, int, bool) { if o.Responses == nil { @@ -104,7 +104,7 @@ func (o *Operation) SuccessResponse() (*Response, int, bool) { } // JSONLookup look up a value by the json property name -func (o Operation) JSONLookup(token string) (interface{}, error) { +func (o Operation) JSONLookup(token string) (any, error) { if ex, ok := o.Extensions[token]; ok { return &ex, nil } @@ -130,18 +130,10 @@ func (o Operation) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - concated := swag.ConcatJSON(b1, b2) + concated := jsonutils.ConcatJSON(b1, b2) return concated, nil } -// NewOperation creates a new operation instance. -// It expects an ID as parameter but not passing an ID is also valid. -func NewOperation(id string) *Operation { - op := new(Operation) - op.ID = id - return op -} - // WithID sets the ID property on this operation, allows for chaining. func (o *Operation) WithID(id string) *Operation { o.ID = id @@ -184,7 +176,7 @@ func (o *Operation) Deprecate() *Operation { return o } -// Undeprecate marks the operation as not deprected +// Undeprecate marks the operation as not deprecated. func (o *Operation) Undeprecate() *Operation { o.Deprecated = false return o diff --git a/operation_test.go b/operation_test.go index 495f609..8e813e8 100644 --- a/operation_test.go +++ b/operation_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -20,13 +9,13 @@ import ( "encoding/json" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) var operation = Operation{ VendorExtensible: VendorExtensible{ - Extensions: map[string]interface{}{ + Extensions: map[string]any{ "x-framework": "go-swagger", }, }, @@ -259,7 +248,7 @@ func TestOperationBuilder(t *testing.T) { func TestIntegrationOperation(t *testing.T) { var actual Operation require.NoError(t, json.Unmarshal([]byte(operationJSON), &actual)) - assert.EqualValues(t, actual, operation) + assert.Equal(t, actual, operation) assertParsesJSON(t, operationJSON, operation) } @@ -354,7 +343,8 @@ func doTestOperationGobEncoding(t *testing.T, fixture string) { doTestAnyGobEncoding(t, &src, &dst) } -func doTestAnyGobEncoding(t *testing.T, src, dst interface{}) { +func doTestAnyGobEncoding(t *testing.T, src, dst any) { + t.Helper() expectedJSON, _ := json.MarshalIndent(src, "", " ") var b bytes.Buffer diff --git a/parameter.go b/parameter.go index bd4f1cd..e0f2cc9 100644 --- a/parameter.go +++ b/parameter.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -19,7 +8,7 @@ import ( "strings" "github.com/go-openapi/jsonpointer" - "github.com/go-openapi/swag" + "github.com/go-openapi/swag/jsonutils" ) // QueryParam creates a query parameter @@ -49,15 +38,21 @@ func FormDataParam(name string) *Parameter { // FileParam creates a body parameter func FileParam(name string) *Parameter { - return &Parameter{ParamProps: ParamProps{Name: name, In: "formData"}, - SimpleSchema: SimpleSchema{Type: "file"}} + return &Parameter{ + ParamProps: ParamProps{Name: name, In: "formData"}, + SimpleSchema: SimpleSchema{Type: "file"}, + } } // SimpleArrayParam creates a param for a simple array (string, int, date etc) func SimpleArrayParam(name, tpe, fmt string) *Parameter { - return &Parameter{ParamProps: ParamProps{Name: name}, - SimpleSchema: SimpleSchema{Type: jsonArray, CollectionFormat: "csv", - Items: &Items{SimpleSchema: SimpleSchema{Type: tpe, Format: fmt}}}} + return &Parameter{ + ParamProps: ParamProps{Name: name}, + SimpleSchema: SimpleSchema{ + Type: jsonArray, CollectionFormat: "csv", + Items: &Items{SimpleSchema: SimpleSchema{Type: tpe, Format: fmt}}, + }, + } } // ParamRef creates a parameter that's a json reference @@ -116,7 +111,7 @@ type Parameter struct { } // JSONLookup look up a value by the json property name -func (p Parameter) JSONLookup(token string) (interface{}, error) { +func (p Parameter) JSONLookup(token string) (any, error) { if ex, ok := p.Extensions[token]; ok { return &ex, nil } @@ -176,7 +171,7 @@ func (p *Parameter) CollectionOf(items *Items, format string) *Parameter { } // WithDefault sets the default value on this parameter -func (p *Parameter) WithDefault(defaultValue interface{}) *Parameter { +func (p *Parameter) WithDefault(defaultValue any) *Parameter { p.AsOptional() // with default implies optional p.Default = defaultValue return p @@ -210,14 +205,14 @@ func (p *Parameter) AsRequired() *Parameter { } // WithMaxLength sets a max length value -func (p *Parameter) WithMaxLength(max int64) *Parameter { - p.MaxLength = &max +func (p *Parameter) WithMaxLength(maximum int64) *Parameter { + p.MaxLength = &maximum return p } // WithMinLength sets a min length value -func (p *Parameter) WithMinLength(min int64) *Parameter { - p.MinLength = &min +func (p *Parameter) WithMinLength(minimum int64) *Parameter { + p.MinLength = &minimum return p } @@ -234,22 +229,22 @@ func (p *Parameter) WithMultipleOf(number float64) *Parameter { } // WithMaximum sets a maximum number value -func (p *Parameter) WithMaximum(max float64, exclusive bool) *Parameter { - p.Maximum = &max +func (p *Parameter) WithMaximum(maximum float64, exclusive bool) *Parameter { + p.Maximum = &maximum p.ExclusiveMaximum = exclusive return p } // WithMinimum sets a minimum number value -func (p *Parameter) WithMinimum(min float64, exclusive bool) *Parameter { - p.Minimum = &min +func (p *Parameter) WithMinimum(minimum float64, exclusive bool) *Parameter { + p.Minimum = &minimum p.ExclusiveMinimum = exclusive return p } // WithEnum sets a the enum values (replace) -func (p *Parameter) WithEnum(values ...interface{}) *Parameter { - p.Enum = append([]interface{}{}, values...) +func (p *Parameter) WithEnum(values ...any) *Parameter { + p.Enum = append([]any{}, values...) return p } @@ -322,5 +317,5 @@ func (p Parameter) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return swag.ConcatJSON(b3, b1, b2, b4, b5), nil + return jsonutils.ConcatJSON(b3, b1, b2, b4, b5), nil } diff --git a/parameters_test.go b/parameters_test.go index 702d867..edb33d6 100644 --- a/parameters_test.go +++ b/parameters_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -18,13 +7,13 @@ import ( "encoding/json" "testing" - "github.com/go-openapi/swag" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/swag/conv" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) var parameter = Parameter{ - VendorExtensible: VendorExtensible{Extensions: map[string]interface{}{ + VendorExtensible: VendorExtensible{Extensions: map[string]any{ "x-framework": "swagger-go", }}, Refable: Refable{Ref: MustCreateRef("Dog")}, @@ -40,7 +29,7 @@ var parameter = Parameter{ MinItems: int64Ptr(5), UniqueItems: true, MultipleOf: float64Ptr(5), - Enum: []interface{}{"hello", "world"}, + Enum: []any{"hello", "world"}, }, SimpleSchema: SimpleSchema{ Type: "string", @@ -94,7 +83,7 @@ var parameterJSON = `{ func TestIntegrationParameter(t *testing.T) { var actual Parameter require.NoError(t, json.Unmarshal([]byte(parameterJSON), &actual)) - assert.EqualValues(t, actual, parameter) + assert.Equal(t, actual, parameter) assertParsesJSON(t, parameterJSON, parameter) } @@ -153,7 +142,6 @@ func TestParameterSerialization(t *testing.T) { assertSerializeJSON(t, BodyParam("", ArrayProperty(RefProperty("Cat"))), `{"in":"body","schema":{"type":"array","items":{"$ref":"Cat"}}}`) - } func TestParameterGobEncoding(t *testing.T) { @@ -163,6 +151,6 @@ func TestParameterGobEncoding(t *testing.T) { } func TestParametersWithValidation(t *testing.T) { - p := new(Parameter).WithValidations(CommonValidations{MaxLength: swag.Int64(15)}) - assert.EqualValues(t, swag.Int64(15), p.MaxLength) + p := new(Parameter).WithValidations(CommonValidations{MaxLength: conv.Pointer(int64(15))}) + assert.Equal(t, conv.Pointer(int64(15)), p.MaxLength) } diff --git a/path_item.go b/path_item.go index 68fc8e9..c692b89 100644 --- a/path_item.go +++ b/path_item.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -18,7 +7,7 @@ import ( "encoding/json" "github.com/go-openapi/jsonpointer" - "github.com/go-openapi/swag" + "github.com/go-openapi/swag/jsonutils" ) // PathItemProps the path item specific properties @@ -46,7 +35,7 @@ type PathItem struct { } // JSONLookup look up a value by the json property name -func (p PathItem) JSONLookup(token string) (interface{}, error) { +func (p PathItem) JSONLookup(token string) (any, error) { if ex, ok := p.Extensions[token]; ok { return &ex, nil } @@ -82,6 +71,6 @@ func (p PathItem) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - concated := swag.ConcatJSON(b3, b4, b5) + concated := jsonutils.ConcatJSON(b3, b4, b5) return concated, nil } diff --git a/path_item_test.go b/path_item_test.go index c3507a0..ddb5e10 100644 --- a/path_item_test.go +++ b/path_item_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -18,14 +7,14 @@ import ( "encoding/json" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) var pathItem = PathItem{ Refable: Refable{Ref: MustCreateRef("Dog")}, VendorExtensible: VendorExtensible{ - Extensions: map[string]interface{}{ + Extensions: map[string]any{ "x-framework": "go-swagger", }, }, @@ -75,7 +64,7 @@ const pathItemJSON = `{ func TestIntegrationPathItem(t *testing.T) { var actual PathItem require.NoError(t, json.Unmarshal([]byte(pathItemJSON), &actual)) - assert.EqualValues(t, actual, pathItem) + assert.Equal(t, actual, pathItem) assertParsesJSON(t, pathItemJSON, pathItem) } diff --git a/paths.go b/paths.go index 9dc82a2..b9e4218 100644 --- a/paths.go +++ b/paths.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -19,7 +8,7 @@ import ( "fmt" "strings" - "github.com/go-openapi/swag" + "github.com/go-openapi/swag/jsonutils" ) // Paths holds the relative paths to the individual endpoints. @@ -30,18 +19,19 @@ import ( // For more information: http://goo.gl/8us55a#pathsObject type Paths struct { VendorExtensible + Paths map[string]PathItem `json:"-"` // custom serializer to flatten this, each entry must start with "/" } // JSONLookup look up a value by the json property name -func (p Paths) JSONLookup(token string) (interface{}, error) { +func (p Paths) JSONLookup(token string) (any, error) { if pi, ok := p.Paths[token]; ok { return &pi, nil } if ex, ok := p.Extensions[token]; ok { return &ex, nil } - return nil, fmt.Errorf("object has no field %q", token) + return nil, fmt.Errorf("object has no field %q: %w", token, ErrSpec) } // UnmarshalJSON hydrates this items instance with the data from JSON @@ -53,9 +43,9 @@ func (p *Paths) UnmarshalJSON(data []byte) error { for k, v := range res { if strings.HasPrefix(strings.ToLower(k), "x-") { if p.Extensions == nil { - p.Extensions = make(map[string]interface{}) + p.Extensions = make(map[string]any) } - var d interface{} + var d any if err := json.Unmarshal(v, &d); err != nil { return err } @@ -92,6 +82,6 @@ func (p Paths) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - concated := swag.ConcatJSON(b1, b2) + concated := jsonutils.ConcatJSON(b1, b2) return concated, nil } diff --git a/paths_test.go b/paths_test.go index 4592818..76868f9 100644 --- a/paths_test.go +++ b/paths_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -18,12 +7,12 @@ import ( "encoding/json" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) var paths = Paths{ - VendorExtensible: VendorExtensible{Extensions: map[string]interface{}{"x-framework": "go-swagger"}}, + VendorExtensible: VendorExtensible{Extensions: map[string]any{"x-framework": "go-swagger"}}, Paths: map[string]PathItem{ "/": { Refable: Refable{Ref: MustCreateRef("cats")}, @@ -36,8 +25,7 @@ const pathsJSON = `{"x-framework":"go-swagger","/":{"$ref":"cats"}}` func TestIntegrationPaths(t *testing.T) { var actual Paths require.NoError(t, json.Unmarshal([]byte(pathsJSON), &actual)) - assert.EqualValues(t, actual, paths) + assert.Equal(t, actual, paths) assertParsesJSON(t, pathsJSON, paths) - } diff --git a/properties.go b/properties.go index 91d2435..4142308 100644 --- a/properties.go +++ b/properties.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package spec import ( @@ -9,8 +12,9 @@ import ( // OrderSchemaItem holds a named schema (e.g. from a property of an object) type OrderSchemaItem struct { - Name string Schema + + Name string } // OrderSchemaItems is a sortable slice of named schemas. diff --git a/properties_test.go b/properties_test.go index d860843..61ca76c 100644 --- a/properties_test.go +++ b/properties_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -29,7 +18,7 @@ func TestPropertySerialization(t *testing.T) { }}, }} - var propSerData = []struct { + propSerData := []struct { Schema *Schema JSON string }{ @@ -54,5 +43,4 @@ func TestPropertySerialization(t *testing.T) { assertSerializeJSON(t, v.Schema, v.JSON) assertParsesJSON(t, v.JSON, v.Schema) } - } diff --git a/ref.go b/ref.go index b0ef9bd..18a29d7 100644 --- a/ref.go +++ b/ref.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -35,16 +24,33 @@ func (r Refable) MarshalJSON() ([]byte, error) { return r.Ref.MarshalJSON() } -// UnmarshalJSON unmarshalss the ref from json +// UnmarshalJSON unmarshals the ref from json. func (r *Refable) UnmarshalJSON(d []byte) error { return json.Unmarshal(d, &r.Ref) } -// Ref represents a json reference that is potentially resolved +// Ref represents a json reference that is potentially resolved. type Ref struct { jsonreference.Ref } +// NewRef creates a new instance of a ref object. +// Returns an error when the reference uri is an invalid uri. +func NewRef(refURI string) (Ref, error) { + ref, err := jsonreference.New(refURI) + if err != nil { + return Ref{}, err + } + + return Ref{Ref: ref}, nil +} + +// MustCreateRef creates a ref object but panics when refURI is invalid. +// Use the NewRef method for a version that returns an error. +func MustCreateRef(refURI string) Ref { + return Ref{Ref: jsonreference.MustCreateRef(refURI)} +} + // RemoteURI gets the remote uri part of the ref func (r *Ref) RemoteURI() string { if r.String() == "" { @@ -75,10 +81,11 @@ func (r *Ref) IsValidURI(basepaths ...string) bool { } defer rr.Body.Close() - return rr.StatusCode/100 == 2 + // true if the response is >= 200 and < 300 + return rr.StatusCode/100 == 2 //nolint:mnd } - if !(r.HasFileScheme || r.HasFullFilePath || r.HasURLPathOnly) { + if !r.HasFileScheme && !r.HasFullFilePath && !r.HasURLPathOnly { return false } @@ -114,22 +121,6 @@ func (r *Ref) Inherits(child Ref) (*Ref, error) { return &Ref{Ref: *ref}, nil } -// NewRef creates a new instance of a ref object -// returns an error when the reference uri is an invalid uri -func NewRef(refURI string) (Ref, error) { - ref, err := jsonreference.New(refURI) - if err != nil { - return Ref{}, err - } - return Ref{Ref: ref}, nil -} - -// MustCreateRef creates a ref object but panics when refURI is invalid. -// Use the NewRef method for a version that returns an error. -func MustCreateRef(refURI string) Ref { - return Ref{Ref: jsonreference.MustCreateRef(refURI)} -} - // MarshalJSON marshals this ref into a JSON object func (r Ref) MarshalJSON() ([]byte, error) { str := r.String() @@ -139,13 +130,13 @@ func (r Ref) MarshalJSON() ([]byte, error) { } return []byte("{}"), nil } - v := map[string]interface{}{"$ref": str} + v := map[string]any{"$ref": str} return json.Marshal(v) } // UnmarshalJSON unmarshals this ref from a JSON object func (r *Ref) UnmarshalJSON(d []byte) error { - var v map[string]interface{} + var v map[string]any if err := json.Unmarshal(d, &v); err != nil { return err } @@ -174,7 +165,7 @@ func (r *Ref) GobDecode(b []byte) error { return json.Unmarshal(raw, r) } -func (r *Ref) fromMap(v map[string]interface{}) error { +func (r *Ref) fromMap(v map[string]any) error { if v == nil { return nil } diff --git a/ref_test.go b/ref_test.go index 6f4c0de..d9e238c 100644 --- a/ref_test.go +++ b/ref_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -20,8 +9,8 @@ import ( "encoding/json" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) // pin pointing go-swagger/go-swagger#1816 issue with cloning ref's @@ -40,5 +29,5 @@ func TestCloneRef(t *testing.T) { jazon, err := json.Marshal(dst) require.NoError(t, err) - assert.Equal(t, `{"$ref":"#/definitions/test"}`, string(jazon)) + assert.JSONEq(t, `{"$ref":"#/definitions/test"}`, string(jazon)) } diff --git a/resolver.go b/resolver.go index 47d1ee1..600574e 100644 --- a/resolver.go +++ b/resolver.go @@ -1,12 +1,15 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package spec import ( "fmt" - "github.com/go-openapi/swag" + "github.com/go-openapi/swag/jsonutils" ) -func resolveAnyWithBase(root interface{}, ref *Ref, result interface{}, options *ExpandOptions) error { +func resolveAnyWithBase(root any, ref *Ref, result any, options *ExpandOptions) error { options = optionsOrDefault(options) resolver := defaultSchemaLoader(root, options, nil, nil) @@ -18,7 +21,7 @@ func resolveAnyWithBase(root interface{}, ref *Ref, result interface{}, options } // ResolveRefWithBase resolves a reference against a context root with preservation of base path -func ResolveRefWithBase(root interface{}, ref *Ref, options *ExpandOptions) (*Schema, error) { +func ResolveRefWithBase(root any, ref *Ref, options *ExpandOptions) (*Schema, error) { result := new(Schema) if err := resolveAnyWithBase(root, ref, result, options); err != nil { @@ -32,7 +35,7 @@ func ResolveRefWithBase(root interface{}, ref *Ref, options *ExpandOptions) (*Sc // ref is guaranteed to be in root (no need to go to external files) // // ResolveRef is ONLY called from the code generation module -func ResolveRef(root interface{}, ref *Ref) (*Schema, error) { +func ResolveRef(root any, ref *Ref) (*Schema, error) { res, _, err := ref.GetPointer().Get(root) if err != nil { return nil, err @@ -43,9 +46,9 @@ func ResolveRef(root interface{}, ref *Ref) (*Schema, error) { return &sch, nil case *Schema: return sch, nil - case map[string]interface{}: + case map[string]any: newSch := new(Schema) - if err = swag.DynamicJSONToStruct(sch, newSch); err != nil { + if err = jsonutils.FromDynamicJSON(sch, newSch); err != nil { return nil, err } return newSch, nil @@ -55,7 +58,7 @@ func ResolveRef(root interface{}, ref *Ref) (*Schema, error) { } // ResolveParameterWithBase resolves a parameter reference against a context root and base path -func ResolveParameterWithBase(root interface{}, ref Ref, options *ExpandOptions) (*Parameter, error) { +func ResolveParameterWithBase(root any, ref Ref, options *ExpandOptions) (*Parameter, error) { result := new(Parameter) if err := resolveAnyWithBase(root, &ref, result, options); err != nil { @@ -66,12 +69,12 @@ func ResolveParameterWithBase(root interface{}, ref Ref, options *ExpandOptions) } // ResolveParameter resolves a parameter reference against a context root -func ResolveParameter(root interface{}, ref Ref) (*Parameter, error) { +func ResolveParameter(root any, ref Ref) (*Parameter, error) { return ResolveParameterWithBase(root, ref, nil) } // ResolveResponseWithBase resolves response a reference against a context root and base path -func ResolveResponseWithBase(root interface{}, ref Ref, options *ExpandOptions) (*Response, error) { +func ResolveResponseWithBase(root any, ref Ref, options *ExpandOptions) (*Response, error) { result := new(Response) err := resolveAnyWithBase(root, &ref, result, options) @@ -83,12 +86,12 @@ func ResolveResponseWithBase(root interface{}, ref Ref, options *ExpandOptions) } // ResolveResponse resolves response a reference against a context root -func ResolveResponse(root interface{}, ref Ref) (*Response, error) { +func ResolveResponse(root any, ref Ref) (*Response, error) { return ResolveResponseWithBase(root, ref, nil) } // ResolvePathItemWithBase resolves response a path item against a context root and base path -func ResolvePathItemWithBase(root interface{}, ref Ref, options *ExpandOptions) (*PathItem, error) { +func ResolvePathItemWithBase(root any, ref Ref, options *ExpandOptions) (*PathItem, error) { result := new(PathItem) if err := resolveAnyWithBase(root, &ref, result, options); err != nil { @@ -101,15 +104,15 @@ func ResolvePathItemWithBase(root interface{}, ref Ref, options *ExpandOptions) // ResolvePathItem resolves response a path item against a context root and base path // // Deprecated: use ResolvePathItemWithBase instead -func ResolvePathItem(root interface{}, ref Ref, options *ExpandOptions) (*PathItem, error) { +func ResolvePathItem(root any, ref Ref, options *ExpandOptions) (*PathItem, error) { return ResolvePathItemWithBase(root, ref, options) } // ResolveItemsWithBase resolves parameter items reference against a context root and base path. // -// NOTE: stricly speaking, this construct is not supported by Swagger 2.0. +// NOTE: strictly speaking, this construct is not supported by Swagger 2.0. // Similarly, $ref are forbidden in response headers. -func ResolveItemsWithBase(root interface{}, ref Ref, options *ExpandOptions) (*Items, error) { +func ResolveItemsWithBase(root any, ref Ref, options *ExpandOptions) (*Items, error) { result := new(Items) if err := resolveAnyWithBase(root, &ref, result, options); err != nil { @@ -122,6 +125,6 @@ func ResolveItemsWithBase(root interface{}, ref Ref, options *ExpandOptions) (*I // ResolveItems resolves parameter items reference against a context root and base path. // // Deprecated: use ResolveItemsWithBase instead -func ResolveItems(root interface{}, ref Ref, options *ExpandOptions) (*Items, error) { +func ResolveItems(root any, ref Ref, options *ExpandOptions) (*Items, error) { return ResolveItemsWithBase(root, ref, options) } diff --git a/resolver_test.go b/resolver_test.go index b1c977a..e8d6311 100644 --- a/resolver_test.go +++ b/resolver_test.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package spec import ( @@ -9,12 +12,12 @@ import ( "testing" "github.com/go-openapi/jsonpointer" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) func TestResolveRef(t *testing.T) { - var root interface{} + var root any require.NoError(t, json.Unmarshal([]byte(PetStore20), &root)) ref, err := NewRef("#/definitions/Category") @@ -409,7 +412,7 @@ func TestResolveExtraItem(t *testing.T) { require.NoError(t, json.Unmarshal(specDoc, spec)) - // Resolve param Items use case: here we explicitly resolve the unsuppord case + // Resolve param Items use case: here we explicitly resolve the unsupported case parm := spec.Paths.Paths["/employees"].Get.Parameters[0] parmItem, err := ResolveItems(spec, parm.Items.Ref, &ExpandOptions{RelativeBase: extraRefFixture}) require.NoError(t, err) @@ -421,7 +424,7 @@ func TestResolveExtraItem(t *testing.T) { "format": "int32" }`, jazon) - // Resolve header Items use case: here we explicitly resolve the unsuppord case + // Resolve header Items use case: here we explicitly resolve the unsupported case hdr := spec.Paths.Paths["/employees"].Get.Responses.StatusCodeResponses[200].Headers["X-header"] hdrItem, err := ResolveItems(spec, hdr.Items.Ref, &ExpandOptions{RelativeBase: extraRefFixture}) require.NoError(t, err) diff --git a/response.go b/response.go index 0340b60..c5ccf96 100644 --- a/response.go +++ b/response.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -18,15 +7,15 @@ import ( "encoding/json" "github.com/go-openapi/jsonpointer" - "github.com/go-openapi/swag" + "github.com/go-openapi/swag/jsonutils" ) // ResponseProps properties specific to a response type ResponseProps struct { - Description string `json:"description"` - Schema *Schema `json:"schema,omitempty"` - Headers map[string]Header `json:"headers,omitempty"` - Examples map[string]interface{} `json:"examples,omitempty"` + Description string `json:"description"` + Schema *Schema `json:"schema,omitempty"` + Headers map[string]Header `json:"headers,omitempty"` + Examples map[string]any `json:"examples,omitempty"` } // Response describes a single response from an API Operation. @@ -38,8 +27,20 @@ type Response struct { VendorExtensible } +// NewResponse creates a new response instance. +func NewResponse() *Response { + return new(Response) +} + +// ResponseRef creates a response as a json reference. +func ResponseRef(url string) *Response { + resp := NewResponse() + resp.Ref = MustCreateRef(url) + return resp +} + // JSONLookup look up a value by the json property name -func (r Response) JSONLookup(token string) (interface{}, error) { +func (r Response) JSONLookup(token string) (any, error) { if ex, ok := r.Extensions[token]; ok { return &ex, nil } @@ -74,14 +75,14 @@ func (r Response) MarshalJSON() ([]byte, error) { } else { // when there is $ref inside the schema, description should be omitempty-ied b1, err = json.Marshal(struct { - Description string `json:"description,omitempty"` - Schema *Schema `json:"schema,omitempty"` - Headers map[string]Header `json:"headers,omitempty"` - Examples map[string]interface{} `json:"examples,omitempty"` + Description string `json:"description,omitempty"` + Schema *Schema `json:"schema,omitempty"` + Headers map[string]Header `json:"headers,omitempty"` + Examples map[string]any `json:"examples,omitempty"` }{ - Description: r.ResponseProps.Description, - Schema: r.ResponseProps.Schema, - Examples: r.ResponseProps.Examples, + Description: r.Description, + Schema: r.Schema, + Examples: r.Examples, }) } if err != nil { @@ -96,19 +97,7 @@ func (r Response) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return swag.ConcatJSON(b1, b2, b3), nil -} - -// NewResponse creates a new response instance -func NewResponse() *Response { - return new(Response) -} - -// ResponseRef creates a response as a json reference -func ResponseRef(url string) *Response { - resp := NewResponse() - resp.Ref = MustCreateRef(url) - return resp + return jsonutils.ConcatJSON(b1, b2, b3), nil } // WithDescription sets the description on this response, allows for chaining @@ -143,9 +132,9 @@ func (r *Response) RemoveHeader(name string) *Response { } // AddExample adds an example to this response -func (r *Response) AddExample(mediaType string, example interface{}) *Response { +func (r *Response) AddExample(mediaType string, example any) *Response { if r.Examples == nil { - r.Examples = make(map[string]interface{}) + r.Examples = make(map[string]any) } r.Examples[mediaType] = example return r diff --git a/response_test.go b/response_test.go index d03720c..cf62d57 100644 --- a/response_test.go +++ b/response_test.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + // Copyright 2017 go-swagger maintainers // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,14 +21,14 @@ import ( "encoding/json" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) var response = Response{ Refable: Refable{Ref: MustCreateRef("Dog")}, VendorExtensible: VendorExtensible{ - Extensions: map[string]interface{}{ + Extensions: map[string]any{ "x-go-name": "PutDogExists", }, }, @@ -47,7 +50,7 @@ const responseJSON = `{ func TestIntegrationResponse(t *testing.T) { var actual Response require.NoError(t, json.Unmarshal([]byte(responseJSON), &actual)) - assert.EqualValues(t, actual, response) + assert.Equal(t, actual, response) assertParsesJSON(t, responseJSON, response) } @@ -61,7 +64,7 @@ func TestJSONLookupResponse(t *testing.T) { var ok bool ref, ok := res.(*Ref) require.True(t, ok) - assert.EqualValues(t, MustCreateRef("Dog"), *ref) + assert.Equal(t, MustCreateRef("Dog"), *ref) var def string res, err = response.JSONLookup("description") @@ -73,13 +76,13 @@ func TestJSONLookupResponse(t *testing.T) { require.True(t, ok) assert.Equal(t, "Dog exists", def) - var x *interface{} + var x *any res, err = response.JSONLookup("x-go-name") require.NoError(t, err) require.NotNil(t, res) require.IsType(t, x, res) - x, ok = res.(*interface{}) + x, ok = res.(*any) require.True(t, ok) assert.EqualValues(t, "PutDogExists", *x) diff --git a/responses.go b/responses.go index 16c3076..733a131 100644 --- a/responses.go +++ b/responses.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -21,7 +10,7 @@ import ( "strconv" "strings" - "github.com/go-openapi/swag" + "github.com/go-openapi/swag/jsonutils" ) // Responses is a container for the expected responses of an operation. @@ -43,7 +32,7 @@ type Responses struct { } // JSONLookup implements an interface to customize json pointer lookup -func (r Responses) JSONLookup(token string) (interface{}, error) { +func (r Responses) JSONLookup(token string) (any, error) { if token == "default" { return r.Default, nil } @@ -55,7 +44,7 @@ func (r Responses) JSONLookup(token string) (interface{}, error) { return scr, nil } } - return nil, fmt.Errorf("object has no field %q", token) + return nil, fmt.Errorf("object has no field %q: %w", token, ErrSpec) } // UnmarshalJSON hydrates this items instance with the data from JSON @@ -83,7 +72,7 @@ func (r Responses) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - concated := swag.ConcatJSON(b1, b2) + concated := jsonutils.ConcatJSON(b1, b2) return concated, nil } diff --git a/responses_test.go b/responses_test.go index fc626bf..f920ad2 100644 --- a/responses_test.go +++ b/responses_test.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + // Copyright 2017 go-swagger maintainers // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,13 +21,13 @@ import ( "encoding/json" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) var responses = Responses{ VendorExtensible: VendorExtensible{ - Extensions: map[string]interface{}{ + Extensions: map[string]any{ "x-go-name": "PutDogExists", }, }, @@ -33,7 +36,7 @@ var responses = Responses{ 200: { Refable: Refable{Ref: MustCreateRef("Dog")}, VendorExtensible: VendorExtensible{ - Extensions: map[string]interface{}{ + Extensions: map[string]any{ "x-go-name": "PutDogExists", }, }, @@ -61,7 +64,7 @@ const responsesJSON = `{ func TestIntegrationResponses(t *testing.T) { var actual Responses require.NoError(t, json.Unmarshal([]byte(responsesJSON), &actual)) - assert.EqualValues(t, actual, responses) + assert.Equal(t, actual, responses) assertParsesJSON(t, responsesJSON, responses) } @@ -77,7 +80,7 @@ func TestJSONLookupResponses(t *testing.T) { ref, ok := res.(*Ref) require.True(t, ok) - assert.EqualValues(t, MustCreateRef("Dog"), *ref) + assert.Equal(t, MustCreateRef("Dog"), *ref) var def string res, err = resp200.JSONLookup("description") @@ -89,13 +92,13 @@ func TestJSONLookupResponses(t *testing.T) { require.True(t, ok) assert.Equal(t, "Dog exists", def) - var x *interface{} + var x *any res, err = responses.JSONLookup("x-go-name") require.NoError(t, err) require.NotNil(t, res) require.IsType(t, x, res) - x, ok = res.(*interface{}) + x, ok = res.(*any) require.True(t, ok) assert.EqualValues(t, "PutDogExists", *x) diff --git a/schema.go b/schema.go index 4e9be85..e294153 100644 --- a/schema.go +++ b/schema.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -20,7 +9,8 @@ import ( "strings" "github.com/go-openapi/jsonpointer" - "github.com/go-openapi/swag" + "github.com/go-openapi/swag/jsonname" + "github.com/go-openapi/swag/jsonutils" ) // BooleanProperty creates a boolean property @@ -88,8 +78,10 @@ func DateTimeProperty() *Schema { // MapProperty creates a map property func MapProperty(property *Schema) *Schema { - return &Schema{SchemaProps: SchemaProps{Type: []string{"object"}, - AdditionalProperties: &SchemaOrBool{Allows: true, Schema: property}}} + return &Schema{SchemaProps: SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &SchemaOrBool{Allows: true, Schema: property}, + }} } // RefProperty creates a ref property @@ -125,20 +117,20 @@ func (r SchemaURL) MarshalJSON() ([]byte, error) { if r == "" { return []byte("{}"), nil } - v := map[string]interface{}{"$schema": string(r)} + v := map[string]any{"$schema": string(r)} return json.Marshal(v) } // UnmarshalJSON unmarshal this from JSON func (r *SchemaURL) UnmarshalJSON(data []byte) error { - var v map[string]interface{} + var v map[string]any if err := json.Unmarshal(data, &v); err != nil { return err } return r.fromMap(v) } -func (r *SchemaURL) fromMap(v map[string]interface{}) error { +func (r *SchemaURL) fromMap(v map[string]any) error { if v == nil { return nil } @@ -165,7 +157,7 @@ type SchemaProps struct { Nullable bool `json:"nullable,omitempty"` Format string `json:"format,omitempty"` Title string `json:"title,omitempty"` - Default interface{} `json:"default,omitempty"` + Default any `json:"default,omitempty"` Maximum *float64 `json:"maximum,omitempty"` ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"` Minimum *float64 `json:"minimum,omitempty"` @@ -177,7 +169,7 @@ type SchemaProps struct { MinItems *int64 `json:"minItems,omitempty"` UniqueItems bool `json:"uniqueItems,omitempty"` MultipleOf *float64 `json:"multipleOf,omitempty"` - Enum []interface{} `json:"enum,omitempty"` + Enum []any `json:"enum,omitempty"` MaxProperties *int64 `json:"maxProperties,omitempty"` MinProperties *int64 `json:"minProperties,omitempty"` Required []string `json:"required,omitempty"` @@ -200,7 +192,7 @@ type SwaggerSchemaProps struct { ReadOnly bool `json:"readOnly,omitempty"` XML *XMLObject `json:"xml,omitempty"` ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"` - Example interface{} `json:"example,omitempty"` + Example any `json:"example,omitempty"` } // Schema the schema object allows the definition of input and output data types. @@ -214,11 +206,12 @@ type Schema struct { VendorExtensible SchemaProps SwaggerSchemaProps - ExtraProps map[string]interface{} `json:"-"` + + ExtraProps map[string]any `json:"-"` } // JSONLookup implements an interface to customize json pointer lookup -func (s Schema) JSONLookup(token string) (interface{}, error) { +func (s Schema) JSONLookup(token string) (any, error) { if ex, ok := s.Extensions[token]; ok { return &ex, nil } @@ -275,14 +268,14 @@ func (s *Schema) WithAllOf(schemas ...Schema) *Schema { } // WithMaxProperties sets the max number of properties an object can have -func (s *Schema) WithMaxProperties(max int64) *Schema { - s.MaxProperties = &max +func (s *Schema) WithMaxProperties(maximum int64) *Schema { + s.MaxProperties = &maximum return s } // WithMinProperties sets the min number of properties an object must have -func (s *Schema) WithMinProperties(min int64) *Schema { - s.MinProperties = &min +func (s *Schema) WithMinProperties(minimum int64) *Schema { + s.MinProperties = &minimum return s } @@ -316,7 +309,7 @@ func (s *Schema) CollectionOf(items Schema) *Schema { } // WithDefault sets the default value on this parameter -func (s *Schema) WithDefault(defaultValue interface{}) *Schema { +func (s *Schema) WithDefault(defaultValue any) *Schema { s.Default = defaultValue return s } @@ -334,14 +327,14 @@ func (s *Schema) AddRequired(items ...string) *Schema { } // WithMaxLength sets a max length value -func (s *Schema) WithMaxLength(max int64) *Schema { - s.MaxLength = &max +func (s *Schema) WithMaxLength(maximum int64) *Schema { + s.MaxLength = &maximum return s } // WithMinLength sets a min length value -func (s *Schema) WithMinLength(min int64) *Schema { - s.MinLength = &min +func (s *Schema) WithMinLength(minimum int64) *Schema { + s.MinLength = &minimum return s } @@ -358,22 +351,22 @@ func (s *Schema) WithMultipleOf(number float64) *Schema { } // WithMaximum sets a maximum number value -func (s *Schema) WithMaximum(max float64, exclusive bool) *Schema { - s.Maximum = &max +func (s *Schema) WithMaximum(maximum float64, exclusive bool) *Schema { + s.Maximum = &maximum s.ExclusiveMaximum = exclusive return s } // WithMinimum sets a minimum number value -func (s *Schema) WithMinimum(min float64, exclusive bool) *Schema { - s.Minimum = &min +func (s *Schema) WithMinimum(minimum float64, exclusive bool) *Schema { + s.Minimum = &minimum s.ExclusiveMinimum = exclusive return s } // WithEnum sets a the enum values (replace) -func (s *Schema) WithEnum(values ...interface{}) *Schema { - s.Enum = append([]interface{}{}, values...) +func (s *Schema) WithEnum(values ...any) *Schema { + s.Enum = append([]any{}, values...) return s } @@ -426,7 +419,7 @@ func (s *Schema) AsWritable() *Schema { } // WithExample sets the example for this schema -func (s *Schema) WithExample(example interface{}) *Schema { +func (s *Schema) WithExample(example any) *Schema { s.Example = example return s } @@ -566,33 +559,33 @@ func (s Schema) Validations() SchemaValidations { func (s Schema) MarshalJSON() ([]byte, error) { b1, err := json.Marshal(s.SchemaProps) if err != nil { - return nil, fmt.Errorf("schema props %v", err) + return nil, fmt.Errorf("schema props: %w: %w", err, ErrSpec) } b2, err := json.Marshal(s.VendorExtensible) if err != nil { - return nil, fmt.Errorf("vendor props %v", err) + return nil, fmt.Errorf("vendor props: %w: %w", err, ErrSpec) } b3, err := s.Ref.MarshalJSON() if err != nil { - return nil, fmt.Errorf("ref prop %v", err) + return nil, fmt.Errorf("ref prop: %w: %w", err, ErrSpec) } b4, err := s.Schema.MarshalJSON() if err != nil { - return nil, fmt.Errorf("schema prop %v", err) + return nil, fmt.Errorf("schema prop: %w: %w", err, ErrSpec) } b5, err := json.Marshal(s.SwaggerSchemaProps) if err != nil { - return nil, fmt.Errorf("common validations %v", err) + return nil, fmt.Errorf("common validations: %w: %w", err, ErrSpec) } var b6 []byte if s.ExtraProps != nil { jj, err := json.Marshal(s.ExtraProps) if err != nil { - return nil, fmt.Errorf("extra props %v", err) + return nil, fmt.Errorf("extra props: %w: %w", err, ErrSpec) } b6 = jj } - return swag.ConcatJSON(b1, b2, b3, b4, b5, b6), nil + return jsonutils.ConcatJSON(b1, b2, b3, b4, b5, b6), nil } // UnmarshalJSON marshal this from JSON @@ -610,7 +603,7 @@ func (s *Schema) UnmarshalJSON(data []byte) error { SwaggerSchemaProps: props.SwaggerSchemaProps, } - var d map[string]interface{} + var d map[string]any if err := json.Unmarshal(data, &d); err != nil { return err } @@ -620,7 +613,7 @@ func (s *Schema) UnmarshalJSON(data []byte) error { delete(d, "$ref") delete(d, "$schema") - for _, pn := range swag.DefaultJSONNameProvider.GetJSONNames(s) { + for _, pn := range jsonname.DefaultJSONNameProvider.GetJSONNames(s) { delete(d, pn) } @@ -628,13 +621,13 @@ func (s *Schema) UnmarshalJSON(data []byte) error { lk := strings.ToLower(k) if strings.HasPrefix(lk, "x-") { if sch.Extensions == nil { - sch.Extensions = map[string]interface{}{} + sch.Extensions = map[string]any{} } sch.Extensions[k] = vv continue } if sch.ExtraProps == nil { - sch.ExtraProps = map[string]interface{}{} + sch.ExtraProps = map[string]any{} } sch.ExtraProps[k] = vv } diff --git a/schema_loader.go b/schema_loader.go index 0059b99..f7b6baf 100644 --- a/schema_loader.go +++ b/schema_loader.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -22,7 +11,9 @@ import ( "reflect" "strings" - "github.com/go-openapi/swag" + "github.com/go-openapi/swag/jsonutils" + "github.com/go-openapi/swag/loading" + "github.com/go-openapi/swag/stringutils" ) // PathLoader is a function to use when loading remote refs. @@ -34,7 +25,7 @@ import ( // this value with its own default (a loader to retrieve YAML documents as // well as JSON ones). var PathLoader = func(pth string) (json.RawMessage, error) { - data, err := swag.LoadFromFileOrHTTP(pth) + data, err := loading.LoadFromFileOrHTTP(pth) if err != nil { return nil, err } @@ -73,12 +64,23 @@ func newResolverContext(options *ExpandOptions) *resolverContext { } type schemaLoader struct { - root interface{} + root any options *ExpandOptions cache ResolutionCache context *resolverContext } +// Resolve resolves a reference against basePath and stores the result in target. +// +// Resolve is not in charge of following references: it only resolves ref by following its URL. +// +// If the schema the ref is referring to holds nested refs, Resolve doesn't resolve them. +// +// If basePath is an empty string, ref is resolved against the root schema stored in the schemaLoader struct. +func (r *schemaLoader) Resolve(ref *Ref, target any, basePath string) error { + return r.resolveRef(ref, target, basePath) +} + func (r *schemaLoader) transitiveResolver(basePath string, ref Ref) *schemaLoader { if ref.IsRoot() || ref.HasFragmentOnly { return r @@ -113,7 +115,7 @@ func (r *schemaLoader) updateBasePath(transitive *schemaLoader, basePath string) return basePath } -func (r *schemaLoader) resolveRef(ref *Ref, target interface{}, basePath string) error { +func (r *schemaLoader) resolveRef(ref *Ref, target any, basePath string) error { tgt := reflect.ValueOf(target) if tgt.Kind() != reflect.Ptr { return ErrResolveRefNeedsAPointer @@ -124,8 +126,8 @@ func (r *schemaLoader) resolveRef(ref *Ref, target interface{}, basePath string) } var ( - res interface{} - data interface{} + res any + data any err error ) @@ -155,10 +157,10 @@ func (r *schemaLoader) resolveRef(ref *Ref, target interface{}, basePath string) return err } } - return swag.DynamicJSONToStruct(res, target) + return jsonutils.FromDynamicJSON(res, target) } -func (r *schemaLoader) load(refURL *url.URL) (interface{}, url.URL, bool, error) { +func (r *schemaLoader) load(refURL *url.URL) (any, url.URL, bool, error) { debugLog("loading schema from url: %s", refURL) toFetch := *refURL toFetch.Fragment = "" @@ -178,7 +180,7 @@ func (r *schemaLoader) load(refURL *url.URL) (interface{}, url.URL, bool, error) return nil, url.URL{}, false, err } - var doc interface{} + var doc any if err := json.Unmarshal(b, &doc); err != nil { return nil, url.URL{}, false, err } @@ -197,25 +199,14 @@ func (r *schemaLoader) isCircular(ref *Ref, basePath string, parentRefs ...strin foundCycle = true return } - foundCycle = swag.ContainsStrings(parentRefs, normalizedRef) // normalized windows url's are lower cased + foundCycle = stringutils.ContainsStrings(parentRefs, normalizedRef) // normalized windows url's are lower cased if foundCycle { r.context.circulars[normalizedRef] = true } return } -// Resolve resolves a reference against basePath and stores the result in target. -// -// Resolve is not in charge of following references: it only resolves ref by following its URL. -// -// If the schema the ref is referring to holds nested refs, Resolve doesn't resolve them. -// -// If basePath is an empty string, ref is resolved against the root schema stored in the schemaLoader struct -func (r *schemaLoader) Resolve(ref *Ref, target interface{}, basePath string) error { - return r.resolveRef(ref, target, basePath) -} - -func (r *schemaLoader) deref(input interface{}, parentRefs []string, basePath string) error { +func (r *schemaLoader) deref(input any, parentRefs []string, basePath string) error { var ref *Ref switch refable := input.(type) { case *Schema: @@ -267,7 +258,7 @@ func (r *schemaLoader) shouldStopOnError(err error) bool { return false } -func (r *schemaLoader) setSchemaID(target interface{}, id, basePath string) (string, string) { +func (r *schemaLoader) setSchemaID(target any, id, basePath string) (string, string) { debugLog("schema has ID: %s", id) // handling the case when id is a folder @@ -299,7 +290,7 @@ func (r *schemaLoader) setSchemaID(target interface{}, id, basePath string) (str } func defaultSchemaLoader( - root interface{}, + root any, expandOptions *ExpandOptions, cache ResolutionCache, context *resolverContext) *schemaLoader { diff --git a/schema_loader_test.go b/schema_loader_test.go index 67a1e79..cc682d7 100644 --- a/schema_loader_test.go +++ b/schema_loader_test.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package spec import ( @@ -5,7 +8,7 @@ import ( "path/filepath" "testing" - "github.com/stretchr/testify/require" + "github.com/go-openapi/testify/v2/require" ) func TestLoader_Issue145(t *testing.T) { diff --git a/schema_test.go b/schema_test.go index 038284c..25264e9 100644 --- a/schema_test.go +++ b/schema_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -18,13 +7,13 @@ import ( "encoding/json" "testing" - "github.com/go-openapi/swag" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/swag/conv" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) var schema = Schema{ - VendorExtensible: VendorExtensible{Extensions: map[string]interface{}{"x-framework": "go-swagger"}}, + VendorExtensible: VendorExtensible{Extensions: map[string]any{"x-framework": "go-swagger"}}, SchemaProps: SchemaProps{ Ref: MustCreateRef("Cat"), Type: []string{"string"}, @@ -43,7 +32,7 @@ var schema = Schema{ MinItems: int64Ptr(5), UniqueItems: true, MultipleOf: float64Ptr(5), - Enum: []interface{}{"hello", "world"}, + Enum: []any{"hello", "world"}, MaxProperties: int64Ptr(5), MinProperties: int64Ptr(1), Required: []string{"id", "name"}, @@ -66,12 +55,12 @@ var schema = Schema{ Description: "the documentation etc", URL: "http://readthedocs.org/swagger", }, - Example: []interface{}{ - map[string]interface{}{ + Example: []any{ + map[string]any{ "id": 1, "name": "a book", }, - map[string]interface{}{ + map[string]any{ "id": 2, "name": "the thing", }, @@ -150,13 +139,12 @@ var schemaJSON = `{ ` func TestSchema(t *testing.T) { - - expected := map[string]interface{}{} + expected := map[string]any{} _ = json.Unmarshal([]byte(schemaJSON), &expected) b, err := json.Marshal(schema) require.NoError(t, err) - var actual map[string]interface{} + var actual map[string]any require.NoError(t, json.Unmarshal(b, &actual)) assert.Equal(t, expected, actual) @@ -193,12 +181,18 @@ func TestSchema(t *testing.T) { assert.Equal(t, schema.AdditionalProperties, actual2.AdditionalProperties) assert.Equal(t, schema.Extensions, actual2.Extensions) - examples := actual2.Example.([]interface{}) - expEx := schema.Example.([]interface{}) - ex1 := examples[0].(map[string]interface{}) - ex2 := examples[1].(map[string]interface{}) - exp1 := expEx[0].(map[string]interface{}) - exp2 := expEx[1].(map[string]interface{}) + examples, ok := actual2.Example.([]any) + assert.True(t, ok, "actual2.Example is not of type []any") + expEx, ok := schema.Example.([]any) + assert.True(t, ok, "schema.Example is not of type []any") + ex1, ok := examples[0].(map[string]any) + assert.True(t, ok, "examples[0] is not of type map[string]any") + ex2, ok := examples[1].(map[string]any) + assert.True(t, ok, "examples[1] is not of type map[string]any") + exp1, ok := expEx[0].(map[string]any) + assert.True(t, ok, "expEx[0] is not of type map[string]any") + exp2, ok := expEx[1].(map[string]any) + assert.True(t, ok, "expEx[1] is not of type map[string]any") assert.EqualValues(t, exp1["id"], ex1["id"]) assert.Equal(t, exp1["name"], ex1["name"]) @@ -207,18 +201,18 @@ func TestSchema(t *testing.T) { } func BenchmarkSchemaUnmarshal(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { sch := &Schema{} _ = sch.UnmarshalJSON([]byte(schemaJSON)) } } func TestSchemaWithValidation(t *testing.T) { - s := new(Schema).WithValidations(SchemaValidations{CommonValidations: CommonValidations{MaxLength: swag.Int64(15)}}) - assert.EqualValues(t, swag.Int64(15), s.MaxLength) + s := new(Schema).WithValidations(SchemaValidations{CommonValidations: CommonValidations{MaxLength: conv.Pointer(int64(15))}}) + assert.Equal(t, conv.Pointer(int64(15)), s.MaxLength) val := mkVal() s = new(Schema).WithValidations(val) - assert.EqualValues(t, val, s.Validations()) + assert.Equal(t, val, s.Validations()) } diff --git a/security_scheme.go b/security_scheme.go index 9d0bdae..46a4a7e 100644 --- a/security_scheme.go +++ b/security_scheme.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -18,7 +7,7 @@ import ( "encoding/json" "github.com/go-openapi/jsonpointer" - "github.com/go-openapi/swag" + "github.com/go-openapi/swag/jsonutils" ) const ( @@ -109,7 +98,7 @@ type SecurityScheme struct { } // JSONLookup implements an interface to customize json pointer lookup -func (s SecurityScheme) JSONLookup(token string) (interface{}, error) { +func (s SecurityScheme) JSONLookup(token string) (any, error) { if ex, ok := s.Extensions[token]; ok { return &ex, nil } @@ -158,7 +147,7 @@ func (s SecurityScheme) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return swag.ConcatJSON(b1, b2), nil + return jsonutils.ConcatJSON(b1, b2), nil } // UnmarshalJSON marshal this from JSON diff --git a/spec.go b/spec.go index 876aa12..b58ce97 100644 --- a/spec.go +++ b/spec.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -24,9 +13,9 @@ import ( //go:generate perl -pi -e s,Json,JSON,g bindata.go const ( - // SwaggerSchemaURL the url for the swagger 2.0 schema to validate specs + // SwaggerSchemaURL the url for the swagger 2.0 schema to validate specs. SwaggerSchemaURL = "http://swagger.io/v2/schema.json#" - // JSONSchemaURL the url for the json schema + // JSONSchemaURL the url for the json schema. JSONSchemaURL = "http://json-schema.org/draft-04/schema#" ) @@ -39,7 +28,7 @@ func MustLoadJSONSchemaDraft04() *Schema { return d } -// JSONSchemaDraft04 loads the json schema document for json shema draft04 +// JSONSchemaDraft04 loads the json schema document for json schema draft04. func JSONSchemaDraft04() (*Schema, error) { b, err := jsonschemaDraft04JSONBytes() if err != nil { @@ -64,7 +53,6 @@ func MustLoadSwagger20Schema() *Schema { // Swagger20Schema loads the swagger 2.0 schema from the embedded assets func Swagger20Schema() (*Schema, error) { - b, err := v2SchemaJSONBytes() if err != nil { return nil, err diff --git a/spec_test.go b/spec_test.go index ae7d492..998c7c7 100644 --- a/spec_test.go +++ b/spec_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec_test @@ -20,8 +9,8 @@ import ( "testing" "github.com/go-openapi/spec" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) // Test unitary fixture for dev and bug fixing @@ -48,7 +37,7 @@ func TestSpec_Issue2743(t *testing.T) { spec.ExpandSpec(sp, &spec.ExpandOptions{RelativeBase: path, SkipSchemas: true, PathLoader: testLoader}), ) - t.Run("all $ref properly reolve when expanding again", func(t *testing.T) { + t.Run("all $ref properly resolve when expanding again", func(t *testing.T) { require.NoError(t, spec.ExpandSpec(sp, &spec.ExpandOptions{RelativeBase: path, SkipSchemas: false, PathLoader: testLoader}), ) @@ -95,7 +84,7 @@ func assertPaths1429(t testing.TB, sp *spec.Swagger) { require.NotNilf(t, param.Schema, "expected param schema not to be nil") // all param fixtures are body param with schema // all $ref expanded - assert.Equal(t, "", param.Schema.Ref.String()) + assert.Empty(t, param.Schema.Ref.String()) } for code, response := range pi.Get.Responses.StatusCodeResponses { @@ -105,7 +94,7 @@ func assertPaths1429(t testing.TB, sp *spec.Swagger) { continue } require.NotNilf(t, response.Schema, "expected response schema not to be nil") - assert.Equal(t, "", response.Schema.Ref.String()) + assert.Empty(t, response.Schema.Ref.String()) } } } @@ -119,7 +108,7 @@ func assertPaths1429SkipSchema(t testing.TB, sp *spec.Swagger) { switch param.Name { case "plainRequest": // this one is expanded - assert.Equal(t, "", param.Schema.Ref.String()) + assert.Empty(t, param.Schema.Ref.String()) continue case "nestedBody": // this one is local @@ -144,7 +133,7 @@ func assertPaths1429SkipSchema(t testing.TB, sp *spec.Swagger) { assert.Contains(t, response.Schema.Ref.String(), "remote/remote.yaml#/") continue case 404: - assert.Equal(t, "", response.Schema.Ref.String()) + assert.Empty(t, response.Schema.Ref.String()) continue } assert.Containsf(t, response.Schema.Ref.String(), "responses.yaml#/", "expected remote ref at resp. %d", code) diff --git a/structs_test.go b/structs_test.go index d464b93..f522952 100644 --- a/structs_test.go +++ b/structs_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -19,27 +8,30 @@ import ( "reflect" "testing" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" + "github.com/go-openapi/testify/v2/assert" + yaml "go.yaml.in/yaml/v3" ) -func assertSerializeJSON(t testing.TB, actual interface{}, expected string) bool { +func assertSerializeJSON(tb testing.TB, actual any, expected string) bool { //nolint:unparam + tb.Helper() ser, err := json.Marshal(actual) if err != nil { - return assert.Fail(t, "unable to marshal to json (%s): %#v", err, actual) + return assert.Failf(tb, "unable to marshal to json", "got: %v: %#v", err, actual) } - return assert.Equal(t, expected, string(ser)) + + return assert.Equal(tb, expected, string(ser)) } -func assertSerializeYAML(t testing.TB, actual interface{}, expected string) bool { +func assertSerializeYAML(tb testing.TB, actual any, expected string) bool { + tb.Helper() ser, err := yaml.Marshal(actual) if err != nil { - return assert.Fail(t, "unable to marshal to yaml (%s): %#v", err, actual) + return assert.Failf(tb, "unable to marshal to yaml", "got: %v: %#v", err, actual) } - return assert.Equal(t, expected, string(ser)) + return assert.Equal(tb, expected, string(ser)) } -func derefTypeOf(expected interface{}) (tpe reflect.Type) { +func derefTypeOf(expected any) (tpe reflect.Type) { tpe = reflect.TypeOf(expected) if tpe.Kind() == reflect.Ptr { tpe = tpe.Elem() @@ -47,7 +39,7 @@ func derefTypeOf(expected interface{}) (tpe reflect.Type) { return } -func isPointed(expected interface{}) (pointed bool) { +func isPointed(expected any) (pointed bool) { tpe := reflect.TypeOf(expected) if tpe.Kind() == reflect.Ptr { pointed = true @@ -55,30 +47,32 @@ func isPointed(expected interface{}) (pointed bool) { return } -func assertParsesJSON(t testing.TB, actual string, expected interface{}) bool { +func assertParsesJSON(tb testing.TB, actual string, expected any) bool { //nolint:unparam + tb.Helper() parsed := reflect.New(derefTypeOf(expected)) err := json.Unmarshal([]byte(actual), parsed.Interface()) if err != nil { - return assert.Fail(t, "unable to unmarshal from json (%s): %s", err, actual) + return assert.Failf(tb, "unable to unmarshal from json", "got: %v: %s", err, actual) } act := parsed.Interface() if !isPointed(expected) { act = reflect.Indirect(parsed).Interface() } - return assert.Equal(t, expected, act) + return assert.Equal(tb, expected, act) } -func assertParsesYAML(t testing.TB, actual string, expected interface{}) bool { +func assertParsesYAML(tb testing.TB, actual string, expected any) bool { + tb.Helper() parsed := reflect.New(derefTypeOf(expected)) err := yaml.Unmarshal([]byte(actual), parsed.Interface()) if err != nil { - return assert.Fail(t, "unable to unmarshal from yaml (%s): %s", err, actual) + return assert.Failf(tb, "unable to unmarshal from yaml", "got: %v: %s", err, actual) } act := parsed.Interface() if !isPointed(expected) { act = reflect.Indirect(parsed).Interface() } - return assert.EqualValues(t, expected, act) + return assert.Equal(tb, expected, act) } func TestSerialization_SerializeJSON(t *testing.T) { @@ -87,13 +81,15 @@ func TestSerialization_SerializeJSON(t *testing.T) { assertSerializeJSON(t, StringOrArray(nil), "null") assertSerializeJSON(t, SchemaOrArray{ Schemas: []Schema{ - {SchemaProps: SchemaProps{Type: []string{"string"}}}}, + {SchemaProps: SchemaProps{Type: []string{"string"}}}, + }, }, "[{\"type\":\"string\"}]") assertSerializeJSON(t, SchemaOrArray{ Schemas: []Schema{ {SchemaProps: SchemaProps{Type: []string{"string"}}}, {SchemaProps: SchemaProps{Type: []string{"string"}}}, - }}, "[{\"type\":\"string\"},{\"type\":\"string\"}]") + }, + }, "[{\"type\":\"string\"},{\"type\":\"string\"}]") assertSerializeJSON(t, SchemaOrArray{}, "null") } @@ -106,8 +102,10 @@ func TestSerialization_DeserializeJSON(t *testing.T) { assertParsesJSON(t, "null", StringOrArray(nil)) // Schema - assertParsesJSON(t, "{\"type\":\"string\"}", SchemaOrArray{Schema: &Schema{ - SchemaProps: SchemaProps{Type: []string{"string"}}}, + assertParsesJSON(t, "{\"type\":\"string\"}", SchemaOrArray{ + Schema: &Schema{ + SchemaProps: SchemaProps{Type: []string{"string"}}, + }, }) assertParsesJSON(t, "[{\"type\":\"string\"},{\"type\":\"string\"}]", &SchemaOrArray{ Schemas: []Schema{ diff --git a/swagger.go b/swagger.go index 1590fd1..a0119dc 100644 --- a/swagger.go +++ b/swagger.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -19,10 +8,11 @@ import ( "encoding/gob" "encoding/json" "fmt" + "slices" "strconv" "github.com/go-openapi/jsonpointer" - "github.com/go-openapi/swag" + "github.com/go-openapi/swag/jsonutils" ) // Swagger this is the root document object for the API specification. @@ -36,7 +26,7 @@ type Swagger struct { } // JSONLookup look up a value by the json property name -func (s Swagger) JSONLookup(token string) (interface{}, error) { +func (s Swagger) JSONLookup(token string) (any, error) { if ex, ok := s.Extensions[token]; ok { return &ex, nil } @@ -54,7 +44,7 @@ func (s Swagger) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return swag.ConcatJSON(b1, b2), nil + return jsonutils.ConcatJSON(b1, b2), nil } // UnmarshalJSON unmarshals a swagger spec from json @@ -227,7 +217,7 @@ type SchemaOrBool struct { } // JSONLookup implements an interface to customize json pointer lookup -func (s SchemaOrBool) JSONLookup(token string) (interface{}, error) { +func (s SchemaOrBool) JSONLookup(token string) (any, error) { if token == "allows" { return s.Allows, nil } @@ -235,8 +225,10 @@ func (s SchemaOrBool) JSONLookup(token string) (interface{}, error) { return r, err } -var jsTrue = []byte("true") -var jsFalse = []byte("false") +var ( + jsTrue = []byte("true") //nolint:gochecknoglobals + jsFalse = []byte("false") //nolint:gochecknoglobals +) // MarshalJSON convert this object to JSON func (s SchemaOrBool) MarshalJSON() ([]byte, error) { @@ -274,7 +266,7 @@ type SchemaOrStringArray struct { } // JSONLookup implements an interface to customize json pointer lookup -func (s SchemaOrStringArray) JSONLookup(token string) (interface{}, error) { +func (s SchemaOrStringArray) JSONLookup(token string) (any, error) { r, _, err := jsonpointer.GetForToken(s.Schema, token) return r, err } @@ -333,16 +325,11 @@ type StringOrArray []string // Contains returns true when the value is contained in the slice func (s StringOrArray) Contains(value string) bool { - for _, str := range s { - if str == value { - return true - } - } - return false + return slices.Contains(s, value) } // JSONLookup implements an interface to customize json pointer lookup -func (s SchemaOrArray) JSONLookup(token string) (interface{}, error) { +func (s SchemaOrArray) JSONLookup(token string) (any, error) { if _, err := strconv.Atoi(token); err == nil { r, _, err := jsonpointer.GetForToken(s.Schemas, token) return r, err @@ -367,7 +354,7 @@ func (s *StringOrArray) UnmarshalJSON(data []byte) error { return nil } - var single interface{} + var single any if err := json.Unmarshal(data, &single); err != nil { return err } @@ -379,7 +366,7 @@ func (s *StringOrArray) UnmarshalJSON(data []byte) error { *s = StringOrArray([]string{v}) return nil default: - return fmt.Errorf("only string or array is allowed, not %T", single) + return fmt.Errorf("only string or array is allowed, not %T: %w", single, ErrSpec) } } diff --git a/swagger_test.go b/swagger_test.go index 59e2403..5c16740 100644 --- a/swagger_test.go +++ b/swagger_test.go @@ -1,27 +1,44 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec import ( "encoding/json" + "fmt" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) +//nolint:gochecknoglobals // it's okay to have embedded test fixtures as globals +var ( + specJSON []byte + minimalJSONSpec []byte + miniJSONSpec []byte +) + +func init() { //nolint:gochecknoinits // it's okay to load embedded fixtures in init(). + // load embedded fixtures + + var err error + specJSON, err = fixtureAssets.ReadFile("fixtures/specs/spec.json") + if err != nil { + panic(fmt.Sprintf("could not find fixture: %v", err)) + } + + minimalJSONSpec, err = fixtureAssets.ReadFile("fixtures/specs/minimal_spec.json") + if err != nil { + panic(fmt.Sprintf("could not find fixture: %v", err)) + } + + miniJSONSpec, err = fixtureAssets.ReadFile("fixtures/specs/mini_spec.json") + if err != nil { + panic(fmt.Sprintf("could not find fixture: %v", err)) + } +} + var spec = Swagger{ SwaggerProps: SwaggerProps{ ID: "http://localhost:3849/api-docs", @@ -29,7 +46,7 @@ var spec = Swagger{ Consumes: []string{"application/json", "application/x-yaml"}, Produces: []string{"application/json"}, Schemes: []string{"http", "https"}, - Info: &info, + Info: &testInfo, Host: "some.api.out.there", BasePath: "/", Paths: &paths, @@ -53,60 +70,12 @@ var spec = Swagger{ Tags: []Tag{NewTag("pets", "", nil)}, ExternalDocs: &ExternalDocumentation{Description: "the name", URL: "the url"}, }, - VendorExtensible: VendorExtensible{Extensions: map[string]interface{}{ + VendorExtensible: VendorExtensible{Extensions: map[string]any{ "x-some-extension": "vendor", - "x-schemes": []interface{}{"unix", "amqp"}, + "x-schemes": []any{"unix", "amqp"}, }}, } -const specJSON = `{ - "id": "http://localhost:3849/api-docs", - "consumes": ["application/json", "application/x-yaml"], - "produces": ["application/json"], - "schemes": ["http", "https"], - "swagger": "2.0", - "info": { - "contact": { - "name": "wordnik api team", - "url": "http://developer.wordnik.com" - }, - "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0` + - ` specification", - "license": { - "name": "Creative Commons 4.0 International", - "url": "http://creativecommons.org/licenses/by/4.0/" - }, - "termsOfService": "http://helloreverb.com/terms/", - "title": "Swagger Sample API", - "version": "1.0.9-abcd", - "x-framework": "go-swagger" - }, - "host": "some.api.out.there", - "basePath": "/", - "paths": {"x-framework":"go-swagger","/":{"$ref":"cats"}}, - "definitions": { "Category": { "type": "string"} }, - "parameters": { - "categoryParam": { - "name": "category", - "in": "query", - "type": "string" - } - }, - "responses": { "EmptyAnswer": { "description": "no data to return for this operation" } }, - "securityDefinitions": { - "internalApiKey": { - "type": "apiKey", - "in": "header", - "name": "api_key" - } - }, - "security": [{"internalApiKey":[]}], - "tags": [{"name":"pets"}], - "externalDocs": {"description":"the name","url":"the url"}, - "x-some-extension": "vendor", - "x-schemes": ["unix","amqp"] -}` - // func verifySpecSerialize(specJSON []byte, spec Swagger) { // expected := map[string]interface{}{} // json.Unmarshal(specJSON, &expected) @@ -129,7 +98,7 @@ const specJSON = `{ expectedType := reflect.TypeOf(expected) if reflect.TypeOf(actual).ConvertibleTo(expectedType) { expectedValue := reflect.ValueOf(expected) - if swag.IsZero(expectedValue) && swag.IsZero(reflect.ValueOf(actual)) { + if typeutil.IsZero(expectedValue) && typeutils.IsZero(reflect.ValueOf(actual)) { return true } @@ -162,7 +131,7 @@ const specJSON = `{ expectedType := reflect.TypeOf(expected) if reflect.TypeOf(actual).ConvertibleTo(expectedType) { expectedValue := reflect.ValueOf(expected) - if swag.IsZero(expectedValue) && swag.IsZero(reflect.ValueOf(actual)) { + if typeutils.IsZero(expectedValue) && typeutils.IsZero(reflect.ValueOf(actual)) { return "" } @@ -237,27 +206,27 @@ func assertSpecJSON(t testing.TB, specJSON []byte) bool { */ func TestSwaggerSpec_Serialize(t *testing.T) { - expected := make(map[string]interface{}) - _ = json.Unmarshal([]byte(specJSON), &expected) + expected := make(map[string]any) + _ = json.Unmarshal(specJSON, &expected) b, err := json.MarshalIndent(spec, "", " ") require.NoError(t, err) - var actual map[string]interface{} + var actual map[string]any require.NoError(t, json.Unmarshal(b, &actual)) - assert.EqualValues(t, expected, actual) + assert.Equal(t, expected, actual) } func TestSwaggerSpec_Deserialize(t *testing.T) { var actual Swagger - require.NoError(t, json.Unmarshal([]byte(specJSON), &actual)) - assert.EqualValues(t, actual, spec) + require.NoError(t, json.Unmarshal(specJSON, &actual)) + assert.Equal(t, actual, spec) } func TestVendorExtensionStringSlice(t *testing.T) { var actual Swagger - require.NoError(t, json.Unmarshal([]byte(specJSON), &actual)) + require.NoError(t, json.Unmarshal(specJSON, &actual)) schemes, ok := actual.Extensions.GetStringSlice("x-schemes") require.True(t, ok) - assert.EqualValues(t, []string{"unix", "amqp"}, schemes) + assert.Equal(t, []string{"unix", "amqp"}, schemes) notSlice, ok := actual.Extensions.GetStringSlice("x-some-extension") assert.Nil(t, notSlice) @@ -268,7 +237,7 @@ func TestVendorExtensionStringSlice(t *testing.T) { assert.Nil(t, notString) assert.False(t, ok) - actual.AddExtension("x-another-slice-ext", []interface{}{100, 100}) + actual.AddExtension("x-another-slice-ext", []any{100, 100}) notStringSlice, ok := actual.Extensions.GetStringSlice("x-another-slice-ext") assert.Nil(t, notStringSlice) assert.False(t, ok) @@ -278,32 +247,13 @@ func TestVendorExtensionStringSlice(t *testing.T) { } func TestOptionalSwaggerProps_Serialize(t *testing.T) { - minimalJSONSpec := []byte(`{ - "swagger": "2.0", - "info": { - "version": "0.0.0", - "title": "Simple API" - }, - "paths": { - "/": { - "get": { - "responses": { - "200": { - "description": "OK" - } - } - } - } - } -}`) - var minimalSpec Swagger - err := json.Unmarshal(minimalJSONSpec, &minimalSpec) + err := json.Unmarshal(miniJSONSpec, &minimalSpec) require.NoError(t, err) bytes, err := json.Marshal(&minimalSpec) require.NoError(t, err) - var ms map[string]interface{} + var ms map[string]any require.NoError(t, json.Unmarshal(bytes, &ms)) assert.NotContains(t, ms, "consumes") @@ -320,51 +270,6 @@ func TestOptionalSwaggerProps_Serialize(t *testing.T) { assert.NotContains(t, ms, "externalDocs") } -var minimalJSONSpec = []byte(`{ - "swagger": "2.0", - "info": { - "version": "0.0.0", - "title": "Simple API" - }, - "securityDefinitions": { - "basic": { - "type": "basic" - }, - "apiKey": { - "type": "apiKey", - "in": "header", - "name": "X-API-KEY" - }, - "queryKey": { - "type": "apiKey", - "in": "query", - "name": "api_key" - } - }, - "paths": { - "/": { - "get": { - "security": [ - { - "apiKey": [], - "basic": [] - }, - {}, - { - "queryKey": [], - "basic": [] - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - } - } - }`) - func TestSecurityRequirements(t *testing.T) { var minimalSpec Swagger require.NoError(t, json.Unmarshal(minimalJSONSpec, &minimalSpec)) @@ -381,12 +286,14 @@ func TestSecurityRequirements(t *testing.T) { func TestSwaggerGobEncoding(t *testing.T) { doTestSwaggerGobEncoding(t, specJSON) - doTestSwaggerGobEncoding(t, string(minimalJSONSpec)) + doTestSwaggerGobEncoding(t, minimalJSONSpec) } -func doTestSwaggerGobEncoding(t *testing.T, fixture string) { +func doTestSwaggerGobEncoding(t *testing.T, fixture []byte) { + t.Helper() + var src, dst Swagger - require.NoError(t, json.Unmarshal([]byte(fixture), &src)) + require.NoError(t, json.Unmarshal(fixture, &src)) doTestAnyGobEncoding(t, &src, &dst) } diff --git a/tag.go b/tag.go index faa3d3d..c4578c1 100644 --- a/tag.go +++ b/tag.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -18,7 +7,7 @@ import ( "encoding/json" "github.com/go-openapi/jsonpointer" - "github.com/go-openapi/swag" + "github.com/go-openapi/swag/jsonutils" ) // TagProps describe a tag entry in the top level tags section of a swagger spec @@ -28,11 +17,6 @@ type TagProps struct { ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"` } -// NewTag creates a new tag -func NewTag(name, description string, externalDocs *ExternalDocumentation) Tag { - return Tag{TagProps: TagProps{Description: description, Name: name, ExternalDocs: externalDocs}} -} - // Tag allows adding meta data to a single tag that is used by the // [Operation Object](http://goo.gl/8us55a#operationObject). // It is not mandatory to have a Tag Object per tag used there. @@ -43,8 +27,13 @@ type Tag struct { TagProps } +// NewTag creates a new tag. +func NewTag(name, description string, externalDocs *ExternalDocumentation) Tag { + return Tag{TagProps: TagProps{Description: description, Name: name, ExternalDocs: externalDocs}} +} + // JSONLookup implements an interface to customize json pointer lookup -func (t Tag) JSONLookup(token string) (interface{}, error) { +func (t Tag) JSONLookup(token string) (any, error) { if ex, ok := t.Extensions[token]; ok { return &ex, nil } @@ -63,7 +52,7 @@ func (t Tag) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return swag.ConcatJSON(b1, b2), nil + return jsonutils.ConcatJSON(b1, b2), nil } // UnmarshalJSON marshal this from JSON diff --git a/url_go19.go b/url_go19.go index 5bdfe40..8d0c81a 100644 --- a/url_go19.go +++ b/url_go19.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package spec import "net/url" diff --git a/validations.go b/validations.go index 6360a8e..2c0dc42 100644 --- a/validations.go +++ b/validations.go @@ -1,19 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package spec // CommonValidations describe common JSON-schema validations type CommonValidations struct { - Maximum *float64 `json:"maximum,omitempty"` - ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"` - Minimum *float64 `json:"minimum,omitempty"` - ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"` - MaxLength *int64 `json:"maxLength,omitempty"` - MinLength *int64 `json:"minLength,omitempty"` - Pattern string `json:"pattern,omitempty"` - MaxItems *int64 `json:"maxItems,omitempty"` - MinItems *int64 `json:"minItems,omitempty"` - UniqueItems bool `json:"uniqueItems,omitempty"` - MultipleOf *float64 `json:"multipleOf,omitempty"` - Enum []interface{} `json:"enum,omitempty"` + Maximum *float64 `json:"maximum,omitempty"` + ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"` + Minimum *float64 `json:"minimum,omitempty"` + ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"` + MaxLength *int64 `json:"maxLength,omitempty"` + MinLength *int64 `json:"minLength,omitempty"` + Pattern string `json:"pattern,omitempty"` + MaxItems *int64 `json:"maxItems,omitempty"` + MinItems *int64 `json:"minItems,omitempty"` + UniqueItems bool `json:"uniqueItems,omitempty"` + MultipleOf *float64 `json:"multipleOf,omitempty"` + Enum []any `json:"enum,omitempty"` } // SetValidations defines all validations for a simple schema. @@ -37,12 +40,12 @@ func (v *CommonValidations) SetValidations(val SchemaValidations) { type clearedValidation struct { Validation string - Value interface{} + Value any } type clearedValidations []clearedValidation -func (c clearedValidations) apply(cbs []func(string, interface{})) { +func (c clearedValidations) apply(cbs []func(string, any)) { for _, cb := range cbs { for _, cleared := range c { cb(cleared.Validation, cleared.Value) @@ -53,8 +56,9 @@ func (c clearedValidations) apply(cbs []func(string, interface{})) { // ClearNumberValidations clears all number validations. // // Some callbacks may be set by the caller to capture changed values. -func (v *CommonValidations) ClearNumberValidations(cbs ...func(string, interface{})) { - done := make(clearedValidations, 0, 5) +func (v *CommonValidations) ClearNumberValidations(cbs ...func(string, any)) { + const maxNumberValidations = 5 + done := make(clearedValidations, 0, maxNumberValidations) defer func() { done.apply(cbs) }() @@ -84,8 +88,9 @@ func (v *CommonValidations) ClearNumberValidations(cbs ...func(string, interface // ClearStringValidations clears all string validations. // // Some callbacks may be set by the caller to capture changed values. -func (v *CommonValidations) ClearStringValidations(cbs ...func(string, interface{})) { - done := make(clearedValidations, 0, 3) +func (v *CommonValidations) ClearStringValidations(cbs ...func(string, any)) { + const maxStringValidations = 3 + done := make(clearedValidations, 0, maxStringValidations) defer func() { done.apply(cbs) }() @@ -107,8 +112,9 @@ func (v *CommonValidations) ClearStringValidations(cbs ...func(string, interface // ClearArrayValidations clears all array validations. // // Some callbacks may be set by the caller to capture changed values. -func (v *CommonValidations) ClearArrayValidations(cbs ...func(string, interface{})) { - done := make(clearedValidations, 0, 3) +func (v *CommonValidations) ClearArrayValidations(cbs ...func(string, any)) { + const maxArrayValidations = 3 + done := make(clearedValidations, 0, maxArrayValidations) defer func() { done.apply(cbs) }() @@ -160,7 +166,7 @@ func (v CommonValidations) HasEnum() bool { // SchemaValidations describes the validation properties of a schema // // NOTE: at this moment, this is not embedded in SchemaProps because this would induce a breaking change -// in the exported members: all initializers using litterals would fail. +// in the exported members: all initializers using literals would fail. type SchemaValidations struct { CommonValidations @@ -194,8 +200,9 @@ func (v SchemaValidations) Validations() SchemaValidations { // ClearObjectValidations returns a clone of the validations with all object validations cleared. // // Some callbacks may be set by the caller to capture changed values. -func (v *SchemaValidations) ClearObjectValidations(cbs ...func(string, interface{})) { - done := make(clearedValidations, 0, 3) +func (v *SchemaValidations) ClearObjectValidations(cbs ...func(string, any)) { + const maxObjectValidations = 3 + done := make(clearedValidations, 0, maxObjectValidations) defer func() { done.apply(cbs) }() diff --git a/validations_test.go b/validations_test.go index 6582921..15b971f 100644 --- a/validations_test.go +++ b/validations_test.go @@ -1,45 +1,47 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package spec import ( "testing" - "github.com/go-openapi/swag" - "github.com/stretchr/testify/require" + "github.com/go-openapi/swag/conv" + "github.com/go-openapi/testify/v2/require" ) func mkVal() SchemaValidations { return SchemaValidations{ CommonValidations: CommonValidations{ - Maximum: swag.Float64(2.5), + Maximum: conv.Pointer(2.5), ExclusiveMaximum: true, - Minimum: swag.Float64(3.4), + Minimum: conv.Pointer(3.4), ExclusiveMinimum: true, - MaxLength: swag.Int64(15), - MinLength: swag.Int64(16), + MaxLength: conv.Pointer(int64(15)), + MinLength: conv.Pointer(int64(16)), Pattern: "abc", - MaxItems: swag.Int64(17), - MinItems: swag.Int64(18), + MaxItems: conv.Pointer(int64(17)), + MinItems: conv.Pointer(int64(18)), UniqueItems: true, - MultipleOf: swag.Float64(4.4), - Enum: []interface{}{"a", 12.5}, + MultipleOf: conv.Pointer(4.4), + Enum: []any{"a", 12.5}, }, PatternProperties: SchemaProperties{ "x": *BooleanProperty(), "y": *BooleanProperty(), }, - MinProperties: swag.Int64(19), - MaxProperties: swag.Int64(20), + MinProperties: conv.Pointer(int64(19)), + MaxProperties: conv.Pointer(int64(20)), } } func TestValidations(t *testing.T) { - var cv CommonValidations val := mkVal() cv.SetValidations(val) expectedCV := val.CommonValidations - require.EqualValues(t, expectedCV, cv) + require.Equal(t, expectedCV, cv) require.True(t, cv.HasArrayValidations()) require.True(t, cv.HasNumberValidations()) @@ -78,16 +80,16 @@ func TestValidations(t *testing.T) { val = mkVal() cv.SetValidations(val) - require.EqualValues(t, expectedSV, cv.Validations()) + require.Equal(t, expectedSV, cv.Validations()) var sv SchemaValidations val = mkVal() sv.SetValidations(val) expectedSV = val - require.EqualValues(t, expectedSV, sv) + require.Equal(t, expectedSV, sv) - require.EqualValues(t, val, sv.Validations()) + require.Equal(t, val, sv.Validations()) require.True(t, sv.HasObjectValidations()) sv.MinProperties = nil @@ -109,7 +111,7 @@ func TestValidations(t *testing.T) { require.False(t, cv.HasArrayValidations()) sv.SetValidations(val) - sv.ClearObjectValidations(func(validation string, _ interface{}) { + sv.ClearObjectValidations(func(validation string, _ any) { switch validation { case "minProperties", "maxProperties", "patternProperties": return diff --git a/xml_object.go b/xml_object.go index 945a467..bf2f8f1 100644 --- a/xml_object.go +++ b/xml_object.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec diff --git a/xml_object_test.go b/xml_object_test.go index 8573e82..f0c46ba 100644 --- a/xml_object_test.go +++ b/xml_object_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package spec @@ -18,8 +7,8 @@ import ( "encoding/json" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) func TestXmlObject_Serialize(t *testing.T) { @@ -39,7 +28,7 @@ func TestXmlObject_Serialize(t *testing.T) { actual, err = json.Marshal(obj2) require.NoError(t, err) - var ad map[string]interface{} + var ad map[string]any require.NoError(t, json.Unmarshal(actual, &ad)) assert.Equal(t, obj2.Name, ad["name"]) assert.Equal(t, obj2.Namespace, ad["namespace"])