diff --git a/.cliff.toml b/.cliff.toml deleted file mode 100644 index 702629f5..00000000 --- a/.cliff.toml +++ /dev/null @@ -1,181 +0,0 @@ -# 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 85707f7e..069bd7e5 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,48 +1,57 @@ -## Contribution Guidelines +You'll find here general guidelines to contribute to this project. +They mostly correspond to standard practices for open source repositories. -You'll find below general guidelines, which mostly correspond to standard practices for open sourced repositories. +We have tried to keep things as simple as possible. ->**TL;DR** -> -> If you're already an experienced go developer on github, then you should just feel at home with us +> [!NOTE] +> If you're 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. +> You'll essentially apply the usual guidelines for a go library project on github. + +These guidelines are common to all libraries published on github by the `go-openapi` organization, +so you'll feel at home with any of our projects. -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][maintainers-doc]. -You'll find more detailed (or repo-specific) instructions in the [maintainer's docs](../docs). +[maintainers-doc]: ../docs/MAINTAINERS.md -## How can I contribute? +## How can I contribute -There are many ways in which you can contribute. Here are a few ideas: +There are many ways in which you can contribute, not just code. 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 +- Reporting issues or bugs +- Suggesting improvements +- Documentation +- Art work that makes the project look great +- Code + - proposing bug fixes and new features that are within the main project scope + - improving test coverage + - addressing code quality issues ## Questions & issues -### Asking questions +### Asking a question + +You may inquire anything about this library by reporting a "Question" issue on github. -You may inquire about anything about this library by reporting a "Question" issue on github. +You may also join our discord server where you may discuss issues or requests. + +[![Discord Server][discord-badge]][discord-url] + +[discord-badge]: https://img.shields.io/discord/1446918742398341256?logo=discord&label=discord&color=blue +[discord-url]: https://discord.gg/twZ9BwT3 ### 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. +Adding a code snippet to reproduce the issue is great, and a big time saver for maintainers. ### Triaging issues @@ -62,14 +71,16 @@ 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 there's a problem with the implementation, hopefully you've 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. +Think that they must be reviewed by a maintainer and it is easy to lose 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, + +Together, these packages constitute a toolkit for go developers: +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. @@ -80,9 +91,11 @@ However, there might be a way to implement that feature *on top of* our librarie 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). +The minimal go compiler version required is always the old stable (latest minor go version - 1). + +Our libraries are designed and tested to work on `Linux`, `MacOS` and `Windows`. -If you're already used to work with `go` you should already have everything in place. +If you're used to work with `go` you should already have everything in place. Although not required, you'll be certainly more productive with a local installation of `golangci-lint`, the meta-linter our CI uses. @@ -104,12 +117,12 @@ 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 +- if it's a bug fixing 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. +NOTE: we don't enforce naming conventions on branches: it's your fork after all. #### Tests @@ -121,10 +134,10 @@ Take a look at existing tests for inspiration, and run the full test suite on yo 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. +Please try your best to cover at least 80% of your patch. #### Code style @@ -132,13 +145,13 @@ You may read our stance on code style [there](../docs/STYLE.md). #### Documentation -Don't forget to update the documentation when creating or modifying features. +Don't forget to update the documentation when creating or modifying a feature. 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: +The documentation for this go-openapi package is published on [the public go docs site][go-doc]. - +--- Check your documentation changes for clarity, concision, and correctness. @@ -150,11 +163,14 @@ 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. +This will run a godoc server locally where you may see the documentation generated from your local repository. + +[go-doc]: https://pkg.go.dev/github.com/go-openapi/spec #### Commit messages @@ -164,7 +180,7 @@ reference to all the issues that they address. Pull requests must not contain commits from other users or branches. 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" ...). +thing to follow that convention (e.g. "fix: fixed panic in XYZ", "ci: did this", "feat: did that" ...). The title in your commit message is used directly to produce our release notes: try to keep them neat. @@ -186,7 +202,7 @@ Be sure to post a comment after pushing. The new commits will show up in the pul 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** +**make sure that you've squashed your commits into logical units of work** using `git rebase -i` and `git push -f`. After every commit the test suite should be passing. @@ -195,6 +211,8 @@ Include documentation changes in the same commit so that a revert would remove a #### Sign your work +Software is developed by real people. + 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. @@ -204,11 +222,30 @@ PGP-signed commit are greatly appreciated but not required. The rules are pretty simple: -* 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 +- read our [DCO][dco-doc] (from [developercertificate.org][dco-source]) +- if you agree with these terms, then you just add a line to every git commit message - Signed-off-by: Joe Smith +``` +Signed-off-by: Joe Smith +``` using your real name (sorry, no pseudonyms or anonymous contributions.) -You can add the sign off when creating the git commit via `git commit -s`. +You can add the sign-off when creating the git commit via `git commit -s`. + +[dco-doc]: ./DCO.md +[dco-source]: https://developercertificate.org + +## Code contributions by AI agents + +Our agentic friends are welcome to contribute! + +We only have a few demands to keep-up with human maintainers. + +1. Issues and PRs written or posted by agents should always mention the original (human) poster for reference +2. We don't accept PRs attributed to agents. We don't want commits signed like "author: @claude.code". + Agents or bots may coauthor commits, though. +3. Security vulnerability reports by agents should always be reported privately and mention the original (human) poster + (see also [Security Policy][security-doc]). + +[security-doc]: ../SECURITY.md diff --git a/.github/DCO.md b/.github/DCO.md index e168dc4c..78a2d64f 100644 --- a/.github/DCO.md +++ b/.github/DCO.md @@ -1,4 +1,4 @@ - # Developer's Certificate of Origin +# Developer's Certificate of Origin ``` Developer Certificate of Origin diff --git a/.github/wordlist.txt b/.github/wordlist.txt new file mode 100644 index 00000000..6dc83168 --- /dev/null +++ b/.github/wordlist.txt @@ -0,0 +1,44 @@ +CodeFactor +CodeQL +DCO +GoDoc +JSON +Maintainer's +PR's +PRs +Repo +SPDX +TODOs +Triaging +UI +XYZ +YAML +agentic +ci +codebase +codecov +config +dependabot +dev +developercertificate +fka +github +godoc +golang +golangci +jsonpointer +jsonschema +linter's +linters +maintainer's +md +metalinter +monorepo +openapi +prepended +repos +semver +sexualized +unmarshal +unmarshaling +vuln diff --git a/.github/workflows/bump-release.yml b/.github/workflows/bump-release.yml index 8d209a6d..0cf81156 100644 --- a/.github/workflows/bump-release.yml +++ b/.github/workflows/bump-release.yml @@ -6,21 +6,15 @@ permissions: on: workflow_dispatch: inputs: - bump-patch: - description: Bump a patch version release - type: boolean + bump-type: + description: Type of bump (patch, minor, major) + type: choice + options: + - patch + - minor + - major + default: patch required: false - default: true - bump-minor: - description: Bump a minor version release - type: boolean - required: false - default: false - bump-major: - description: Bump a major version release - type: boolean - required: false - default: false tag-message-title: description: Tag message title to prepend to the release notes required: false @@ -38,9 +32,7 @@ jobs: contents: write uses: go-openapi/ci-workflows/.github/workflows/bump-release.yml@84f8f9c0759d5d1d0c32b18a7abaa0cba65ebcff # v0.2.9 with: - bump-patch: ${{ inputs.bump-patch }} - bump-minor: ${{ inputs.bump-minor }} - bump-major: ${{ inputs.bump-major }} + bump-type: ${{ inputs.bump-type }} tag-message-title: ${{ inputs.tag-message-title }} tag-message-body: ${{ inputs.tag-message-body }} secrets: inherit diff --git a/.gitignore b/.gitignore index f47cb204..885dc27a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,6 @@ *.out +*.cov +.idea +.env +.mcp.json +.claude/ diff --git a/.golangci.yml b/.golangci.yml index fdae591b..dc7c9605 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -12,6 +12,7 @@ linters: - paralleltest - recvcheck - testpackage + - thelper - tparallel - varnamelen - whitespace diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 9322b065..bac878f2 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -23,7 +23,9 @@ include: Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or + advances + * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic @@ -55,7 +57,7 @@ further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at ivan+abuse@flanders.co.nz. All +reported by contacting the project team at . All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. @@ -68,7 +70,7 @@ members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] +available at [][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 26b16576..2967e3ce 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -8,43 +8,43 @@ | Username | All Time Contribution Count | All Commits | | --- | --- | --- | -| @casualjim | 191 | https://github.com/go-openapi/spec/commits?author=casualjim | -| @fredbi | 90 | 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 | +| @casualjim | 191 | | +| @fredbi | 90 | | +| @pytlesk4 | 26 | | +| @kul-amr | 10 | | +| @keramix | 10 | | +| @youyuanwu | 8 | | +| @pengsrc | 7 | | +| @alphacentory | 5 | | +| @mtfelian | 4 | | +| @Capstan | 4 | | +| @sdghchj | 4 | | +| @databus23 | 2 | | +| @vburenin | 2 | | +| @petrkotas | 2 | | +| @nikhita | 2 | | +| @hypnoglow | 2 | | +| @carvind | 2 | | +| @ujjwalsh | 1 | | +| @mbohlool | 1 | | +| @j2gg0s | 1 | | +| @ishveda | 1 | | +| @micln | 1 | | +| @GlenDC | 1 | | +| @agmikhailov | 1 | | +| @tgraf | 1 | | +| @zhsj | 1 | | +| @sebastien-rosset | 1 | | +| @alexandear | 1 | | +| @morlay | 1 | | +| @mikedanese | 1 | | +| @koron | 1 | | +| @honza | 1 | | +| @gbjk | 1 | | +| @faguirre1 | 1 | | +| @ethantkoenig | 1 | | +| @sttts | 1 | | +| @ChandanChainani | 1 | | +| @bvwells | 1 | | _this file was generated by the [Contributors GitHub Action](https://github.com/github/contributors)_ diff --git a/README.md b/README.md index 13a2a17e..134809fd 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ go get github.com/go-openapi/spec > There is no plan to make it evolve toward supporting OpenAPI 3.x. > This [discussion thread](https://github.com/go-openapi/spec/issues/21) relates the full story. > -> An early attempt to support Swagger 3 may be found at: https://github.com/go-openapi/spec3 +> An early attempt to support Swagger 3 may be found at: * Does the unmarshaling support YAML? @@ -64,13 +64,13 @@ go get github.com/go-openapi/spec > In order to load a YAML document as a Swagger spec, you need to use the loaders provided by > github.com/go-openapi/loads > -> Take a look at the example there: https://pkg.go.dev/github.com/go-openapi/loads#example-Spec +> Take a look at the example there: > -> See also https://github.com/go-openapi/spec/issues/164 +> See also * How can I validate a spec? -> Validation is provided by [the validate package](http://github.com/go-openapi/validate) +Validation is provided by [the validate package](http://github.com/go-openapi/validate) * Why do we have an `ID` field for `Schema` which is not part of the swagger spec? @@ -78,7 +78,7 @@ go get github.com/go-openapi/spec > how `$ref` are resolved. > This `id` does not conflict with any property named `id`. > -> See also https://github.com/go-openapi/spec/issues/23 +> See also ## Change log @@ -136,7 +136,7 @@ Maintainers can cut a new release by either: [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 [discord-badge]: https://img.shields.io/discord/1446918742398341256?logo=discord&label=discord&color=blue -[discord-url]: https://discord.gg/DrafRmZx +[discord-url]: https://discord.gg/twZ9BwT3 [license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg diff --git a/SECURITY.md b/SECURITY.md index 2a7b6f09..1fea2c57 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,14 +6,32 @@ This policy outlines the commitment and practices of the go-openapi maintainers | Version | Supported | | ------- | ------------------ | -| 0.22.x | :white_check_mark: | +| O.x | :white_check_mark: | + +## Vulnerability checks in place + +This repository uses automated vulnerability scans, at every merged commit and at least once a week. + +We use: + +* [`GitHub CodeQL`][codeql-url] +* [`trivy`][trivy-url] +* [`govulncheck`][govulncheck-url] + +Reports are centralized in github security reports and visible only to the maintainers. ## Reporting a vulnerability If you become aware of a security vulnerability that affects the current repository, -please report it privately to the maintainers. +**please report it privately to the maintainers** +rather than opening a publicly visible GitHub issue. + +Please follow the instructions provided by github to [Privately report a security vulnerability][github-guidance-url]. -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). +> [!NOTE] +> On Github, navigate to the project's "Security" tab then click on "Report a vulnerability". -TL;DR: on Github, navigate to the project's "Security" tab then click on "Report a vulnerability". +[codeql-url]: https://github.com/github/codeql +[trivy-url]: https://trivy.dev/docs/latest/getting-started +[govulncheck-url]: https://go.dev/blog/govulncheck +[github-guidance-url]: 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 diff --git a/auth_test.go b/auth_test.go index df719f23..486d74de 100644 --- a/auth_test.go +++ b/auth_test.go @@ -5,120 +5,103 @@ package spec import ( "testing" + + "github.com/go-openapi/testify/v2/assert" ) func TestSerialization_AuthSerialization(t *testing.T) { - assertSerializeJSON(t, BasicAuth(), `{"type":"basic"}`) + assert.JSONMarshalAsT(t, `{"type":"basic"}`, BasicAuth()) - assertSerializeJSON(t, APIKeyAuth("api-key", "header"), `{"type":"apiKey","name":"api-key","in":"header"}`) + assert.JSONMarshalAsT(t, `{"type":"apiKey","name":"api-key","in":"header"}`, APIKeyAuth("api-key", "header")) - assertSerializeJSON( - t, - OAuth2Implicit("http://foo.com/authorization"), - `{"type":"oauth2","flow":"implicit","authorizationUrl":"http://foo.com/authorization"}`) + assert.JSONMarshalAsT(t, + `{"type":"oauth2","flow":"implicit","authorizationUrl":"http://foo.com/authorization"}`, + OAuth2Implicit("http://foo.com/authorization")) - assertSerializeJSON( - t, - OAuth2Password("http://foo.com/token"), - `{"type":"oauth2","flow":"password","tokenUrl":"http://foo.com/token"}`) + assert.JSONMarshalAsT(t, + `{"type":"oauth2","flow":"password","tokenUrl":"http://foo.com/token"}`, + OAuth2Password("http://foo.com/token")) - assertSerializeJSON(t, - OAuth2Application("http://foo.com/token"), - `{"type":"oauth2","flow":"application","tokenUrl":"http://foo.com/token"}`) + assert.JSONMarshalAsT(t, + `{"type":"oauth2","flow":"application","tokenUrl":"http://foo.com/token"}`, + OAuth2Application("http://foo.com/token")) - assertSerializeJSON( - t, - OAuth2AccessToken("http://foo.com/authorization", "http://foo.com/token"), + assert.JSONMarshalAsT(t, `{"type":"oauth2","flow":"accessCode","authorizationUrl":"http://foo.com/authorization",`+ - `"tokenUrl":"http://foo.com/token"}`) + `"tokenUrl":"http://foo.com/token"}`, + OAuth2AccessToken("http://foo.com/authorization", "http://foo.com/token")) auth1 := OAuth2Implicit("http://foo.com/authorization") auth1.AddScope("email", "read your email") - assertSerializeJSON( - t, - auth1, + assert.JSONMarshalAsT(t, `{"type":"oauth2","flow":"implicit","authorizationUrl":"http://foo.com/authorization",`+ - `"scopes":{"email":"read your email"}}`) + `"scopes":{"email":"read your email"}}`, + auth1) auth2 := OAuth2Password("http://foo.com/authorization") auth2.AddScope("email", "read your email") - assertSerializeJSON( - t, - auth2, + assert.JSONMarshalAsT(t, `{"type":"oauth2","flow":"password","tokenUrl":"http://foo.com/authorization",`+ - `"scopes":{"email":"read your email"}}`) + `"scopes":{"email":"read your email"}}`, + auth2) auth3 := OAuth2Application("http://foo.com/token") auth3.AddScope("email", "read your email") - assertSerializeJSON( - t, - auth3, - `{"type":"oauth2","flow":"application","tokenUrl":"http://foo.com/token","scopes":{"email":"read your email"}}`) + assert.JSONMarshalAsT(t, + `{"type":"oauth2","flow":"application","tokenUrl":"http://foo.com/token","scopes":{"email":"read your email"}}`, + auth3) auth4 := OAuth2AccessToken("http://foo.com/authorization", "http://foo.com/token") auth4.AddScope("email", "read your email") - assertSerializeJSON( - t, - auth4, + assert.JSONMarshalAsT(t, `{"type":"oauth2","flow":"accessCode","authorizationUrl":"http://foo.com/authorization",`+ - `"tokenUrl":"http://foo.com/token","scopes":{"email":"read your email"}}`) + `"tokenUrl":"http://foo.com/token","scopes":{"email":"read your email"}}`, + auth4) } func TestSerialization_AuthDeserialization(t *testing.T) { + assert.JSONUnmarshalAsT(t, BasicAuth(), `{"type":"basic"}`) - assertParsesJSON(t, `{"type":"basic"}`, BasicAuth()) - - assertParsesJSON( - t, - `{"in":"header","name":"api-key","type":"apiKey"}`, - APIKeyAuth("api-key", "header")) + assert.JSONUnmarshalAsT(t, + APIKeyAuth("api-key", "header"), + `{"in":"header","name":"api-key","type":"apiKey"}`) - assertParsesJSON( - t, - `{"authorizationUrl":"http://foo.com/authorization","flow":"implicit","type":"oauth2"}`, - OAuth2Implicit("http://foo.com/authorization")) + assert.JSONUnmarshalAsT(t, + OAuth2Implicit("http://foo.com/authorization"), + `{"authorizationUrl":"http://foo.com/authorization","flow":"implicit","type":"oauth2"}`) - assertParsesJSON( - t, - `{"flow":"password","tokenUrl":"http://foo.com/token","type":"oauth2"}`, - OAuth2Password("http://foo.com/token")) + assert.JSONUnmarshalAsT(t, + OAuth2Password("http://foo.com/token"), + `{"flow":"password","tokenUrl":"http://foo.com/token","type":"oauth2"}`) - assertParsesJSON( - t, - `{"flow":"application","tokenUrl":"http://foo.com/token","type":"oauth2"}`, - OAuth2Application("http://foo.com/token")) + assert.JSONUnmarshalAsT(t, + OAuth2Application("http://foo.com/token"), + `{"flow":"application","tokenUrl":"http://foo.com/token","type":"oauth2"}`) - assertParsesJSON( - t, + assert.JSONUnmarshalAsT(t, + OAuth2AccessToken("http://foo.com/authorization", "http://foo.com/token"), `{"authorizationUrl":"http://foo.com/authorization","flow":"accessCode","tokenUrl":"http://foo.com/token",`+ - `"type":"oauth2"}`, - OAuth2AccessToken("http://foo.com/authorization", "http://foo.com/token")) + `"type":"oauth2"}`) auth1 := OAuth2Implicit("http://foo.com/authorization") auth1.AddScope("email", "read your email") - assertParsesJSON(t, + assert.JSONUnmarshalAsT(t, auth1, `{"authorizationUrl":"http://foo.com/authorization","flow":"implicit","scopes":{"email":"read your email"},`+ - `"type":"oauth2"}`, - auth1) + `"type":"oauth2"}`) auth2 := OAuth2Password("http://foo.com/token") auth2.AddScope("email", "read your email") - assertParsesJSON(t, - `{"flow":"password","scopes":{"email":"read your email"},"tokenUrl":"http://foo.com/token","type":"oauth2"}`, - auth2) + assert.JSONUnmarshalAsT(t, auth2, + `{"flow":"password","scopes":{"email":"read your email"},"tokenUrl":"http://foo.com/token","type":"oauth2"}`) auth3 := OAuth2Application("http://foo.com/token") auth3.AddScope("email", "read your email") - assertParsesJSON(t, - `{"flow":"application","scopes":{"email":"read your email"},"tokenUrl":"http://foo.com/token","type":"oauth2"}`, - auth3) + assert.JSONUnmarshalAsT(t, auth3, + `{"flow":"application","scopes":{"email":"read your email"},"tokenUrl":"http://foo.com/token","type":"oauth2"}`) auth4 := OAuth2AccessToken("http://foo.com/authorization", "http://foo.com/token") auth4.AddScope("email", "read your email") - assertParsesJSON( - t, + assert.JSONUnmarshalAsT(t, auth4, `{"authorizationUrl":"http://foo.com/authorization","flow":"accessCode","scopes":{"email":"read your email"},`+ - `"tokenUrl":"http://foo.com/token","type":"oauth2"}`, - auth4) - + `"tokenUrl":"http://foo.com/token","type":"oauth2"}`) } diff --git a/cache.go b/cache.go index 10fba77a..06495d2c 100644 --- a/cache.go +++ b/cache.go @@ -8,10 +8,10 @@ import ( "sync" ) -// ResolutionCache a cache for resolving urls +// ResolutionCache a cache for resolving urls. type ResolutionCache interface { - Get(string) (any, bool) - Set(string, any) + Get(uri string) (any, bool) + Set(uri string, data any) } type simpleCache struct { @@ -19,7 +19,7 @@ type simpleCache struct { store map[string]any } -func (s *simpleCache) ShallowClone() ResolutionCache { +func (s *simpleCache) ShallowClone() ResolutionCache { //nolint:ireturn // returns the public interface type by design store := make(map[string]any, len(s.store)) s.lock.RLock() maps.Copy(store, s.store) @@ -30,7 +30,7 @@ func (s *simpleCache) ShallowClone() ResolutionCache { } } -// Get retrieves a cached URI +// Get retrieves a cached URI. func (s *simpleCache) Get(uri string) (any, bool) { s.lock.RLock() v, ok := s.store[uri] @@ -39,7 +39,7 @@ func (s *simpleCache) Get(uri string) (any, bool) { return v, ok } -// Set caches a URI +// Set caches a URI. func (s *simpleCache) Set(uri string, data any) { s.lock.Lock() s.store[uri] = data @@ -56,8 +56,8 @@ var ( // // All subsequent utilizations of this cache are produced from a shallow // clone of this initial version. - resCache *simpleCache - onceCache sync.Once + resCache *simpleCache //nolint:gochecknoglobals // package-level lazy cache for $ref resolution + onceCache sync.Once //nolint:gochecknoglobals // guards lazy init of resCache _ ResolutionCache = &simpleCache{} ) @@ -74,7 +74,7 @@ func defaultResolutionCache() *simpleCache { }} } -func cacheOrDefault(cache ResolutionCache) ResolutionCache { +func cacheOrDefault(cache ResolutionCache) ResolutionCache { //nolint:ireturn // returns the public interface type by design onceCache.Do(initResolutionCache) if cache != nil { diff --git a/cache_test.go b/cache_test.go index 1ef15db1..92f8a8f6 100644 --- a/cache_test.go +++ b/cache_test.go @@ -16,19 +16,19 @@ func TestDefaultResolutionCache(t *testing.T) { cache := defaultResolutionCache() sch, ok := cache.Get("not there") - assert.False(t, ok) + assert.FalseT(t, ok) assert.Nil(t, sch) sch, ok = cache.Get("http://swagger.io/v2/schema.json") - assert.True(t, ok) + assert.TrueT(t, ok) assert.Equal(t, swaggerSchema, sch) sch, ok = cache.Get("http://json-schema.org/draft-04/schema") - assert.True(t, ok) + assert.TrueT(t, ok) assert.Equal(t, jsonSchema, sch) cache.Set("something", "here") sch, ok = cache.Get("something") - assert.True(t, ok) + assert.TrueT(t, ok) assert.Equal(t, "here", sch) } diff --git a/circular_test.go b/circular_test.go index cc607c24..5a8535c7 100644 --- a/circular_test.go +++ b/circular_test.go @@ -6,7 +6,6 @@ package spec import ( "encoding/json" "net/http" - "net/http/httptest" "os" "path/filepath" "testing" @@ -73,7 +72,7 @@ func TestExpandCircular_Spec2Expansion(t *testing.T) { assertRefResolve(t, jazon, "", root) // assert stripped $ref in result - assert.NotContainsf(t, jazon, "circular-minimal.json#/", + assert.StringNotContainsTf(t, jazon, "circular-minimal.json#/", "expected %s to be expanded with stripped circular $ref", fixturePath) fixturePath = filepath.Join("fixtures", "expansion", "circularSpec2.json") @@ -89,7 +88,7 @@ func TestExpandCircular_Spec2Expansion(t *testing.T) { // circular $ref can always be further expanded against the root assertRefExpand(t, jazon, "", root) - assert.NotContainsf(t, jazon, "circularSpec.json#/", + assert.StringNotContainsTf(t, jazon, "circularSpec.json#/", "expected %s to be expanded with stripped circular $ref", fixturePath) /* @@ -152,7 +151,7 @@ func TestExpandCircular_Issue957(t *testing.T) { jazon, root := expandThisOrDieTrying(t, fixturePath) require.NotEmpty(t, jazon) - require.NotContainsf(t, jazon, "fixture-957.json#/", + require.StringNotContainsTf(t, jazon, "fixture-957.json#/", "expected %s to be expanded with stripped circular $ref", fixturePath) assertRefInJSON(t, jazon, "#/definitions/") @@ -253,9 +252,9 @@ func TestExpandCircular_RemoteCircularID(t *testing.T) { } func TestCircular_RemoteExpandAzure(t *testing.T) { - // local copy of : https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/network/resource-manager/Microsoft.Network/stable/2020-04-01/publicIpAddress.json - server := httptest.NewServer(http.FileServer(http.Dir("fixtures/azure"))) - defer server.Close() + // local copy of Azure publicIpAddress.json from azure-rest-api-specs + // (Microsoft.Network/stable/2020-04-01) + server := fixtureServer(t, "fixtures/azure") basePath := server.URL + "/publicIpAddress.json" jazon, sch := expandThisOrDieTrying(t, basePath) diff --git a/contact_info.go b/contact_info.go index fafe639b..46fada5d 100644 --- a/contact_info.go +++ b/contact_info.go @@ -17,14 +17,14 @@ type ContactInfo struct { VendorExtensible } -// ContactInfoProps hold the properties of a ContactInfo object +// ContactInfoProps hold the properties of a ContactInfo object. type ContactInfoProps struct { Name string `json:"name,omitempty"` URL string `json:"url,omitempty"` Email string `json:"email,omitempty"` } -// UnmarshalJSON hydrates ContactInfo from json +// UnmarshalJSON hydrates ContactInfo from json. func (c *ContactInfo) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &c.ContactInfoProps); err != nil { return err @@ -32,7 +32,7 @@ func (c *ContactInfo) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &c.VendorExtensible) } -// MarshalJSON produces ContactInfo as json +// MarshalJSON produces ContactInfo as json. func (c ContactInfo) MarshalJSON() ([]byte, error) { b1, err := json.Marshal(c.ContactInfoProps) if err != nil { diff --git a/contact_info_test.go b/contact_info_test.go index 6159f75d..bcf177e2 100644 --- a/contact_info_test.go +++ b/contact_info_test.go @@ -4,11 +4,9 @@ package spec import ( - "encoding/json" "testing" "github.com/go-openapi/testify/v2/assert" - "github.com/go-openapi/testify/v2/require" ) const contactInfoJSON = `{ @@ -18,19 +16,13 @@ const contactInfoJSON = `{ "x-teams": "test team" }` -var contactInfo = ContactInfo{ContactInfoProps: ContactInfoProps{ +var contactInfo = ContactInfo{ContactInfoProps: ContactInfoProps{ //nolint:gochecknoglobals // test fixture Name: "wordnik api team", URL: "http://developer.wordnik.com", Email: "some@mailayada.dkdkd", }, 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.JSONEq(t, contactInfoJSON, string(b)) - - actual := ContactInfo{} - err = json.Unmarshal([]byte(contactInfoJSON), &actual) - require.NoError(t, err) - assert.Equal(t, contactInfo, actual) + assert.JSONMarshalAsT(t, contactInfoJSON, contactInfo) + assert.JSONUnmarshalAsT(t, contactInfo, contactInfoJSON) } diff --git a/debug.go b/debug.go index f4316c26..fa52b0c7 100644 --- a/debug.go +++ b/debug.go @@ -14,14 +14,12 @@ import ( // Debug is true when the SWAGGER_DEBUG env var is not empty. // // It enables a more verbose logging of this package. -var Debug = os.Getenv("SWAGGER_DEBUG") != "" +var Debug = os.Getenv("SWAGGER_DEBUG") != "" //nolint:gochecknoglobals // public toggle for debug logging -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 // package-level debug logger -func init() { +func init() { //nolint:gochecknoinits // initializes debug logger at package load debugOptions() } diff --git a/debug_test.go b/debug_test.go index 0a8ba089..6f6547bf 100644 --- a/debug_test.go +++ b/debug_test.go @@ -11,9 +11,7 @@ import ( "github.com/go-openapi/testify/v2/assert" ) -var ( - logMutex = &sync.Mutex{} -) +var logMutex = &sync.Mutex{} //nolint:gochecknoglobals // test fixture func TestDebug(t *testing.T) { // usetesting linter disabled until https://github.com/golang/go/issues/71544 is fixed for windows @@ -40,9 +38,9 @@ func TestDebug(t *testing.T) { Debug = false _ = tmpFile.Close() - flushed, _ := os.Open(tmpName) + flushed, _ := os.Open(tmpName) //nolint:gosec // test file, path is from os.CreateTemp buf := make([]byte, 500) _, _ = flushed.Read(buf) specLogger.SetOutput(os.Stdout) - assert.Contains(t, string(buf), "A debug") + assert.StringContainsT(t, string(buf), "A debug") } diff --git a/docs/MAINTAINERS.md b/docs/MAINTAINERS.md index 6c15d12c..8fc6befb 100644 --- a/docs/MAINTAINERS.md +++ b/docs/MAINTAINERS.md @@ -1,37 +1,33 @@ -# Maintainer's guide +> [!NOTE] +> Comprehensive guide for maintainers covering repository structure, CI/CD workflows, release procedures, and development practices. +> Essential reading for anyone contributing to or maintaining this project. ## Repo structure -Single go module. - -> **NOTE** -> -> Some `go-openapi` repos are mono-repos with multiple modules, -> with adapted CI workflows. +This project is organized as a repo with a single go module. ## Repo configuration -* default branch: master -* protected branches: master -* branch protection rules: +* 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) + * required status checks: + * DCO (simple email sign-off) + * Lint + * All tests completed +* Auto-merge enabled (used for dependabot updates and other auto-merged PR's, e.g. contributors update) ## 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) +* meta-linter: [golangci-lint][golangci-url] +* linter config: [`.golangci.yml`][linter-config] (see our [posture][style-doc] on linters) +* Code quality assessment: [CodeFactor][codefactor-url] * Code quality badges - * go report card: - * CodeFactor: + * [go report card][gocard-url] + * [CodeFactor][codefactor-url] > **NOTES** > @@ -58,7 +54,7 @@ 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`. +All tests across `go-openapi` use our fork of `stretchr/spec` (this repo): `github.com/go-openapi/spec`. This allows for minimal test dependencies. > **NOTES** @@ -76,7 +72,7 @@ This allows for minimal test dependencies. ### Automated updates * dependabot - * configuration: [`dependabot.yaml`](../.github/dependabot.yaml) + * configuration: [`dependabot.yaml`][dependabot-config] Principle: @@ -84,7 +80,7 @@ This allows for minimal test dependencies. * all updates from "trusted" dependencies (github actions, golang.org packages, go-openapi packages are auto-merged if they successfully pass CI. -* go version udpates +* go version updates Principle: @@ -92,8 +88,14 @@ This allows for minimal test dependencies. * `go.mod` should be updated (manually) whenever there is a new go minor release (e.g. every 6 months). + > This means that our projects always have a 6 months lag to enforce new features from the go compiler. + > + > However, new features of go may be used with a "go:build" tag: this allows users of the newer + > version to benefit the new feature while users still running with `oldstable` use another version + > that still builds. + * contributors - * a [`CONTRIBUTORS.md`](../CONTRIBUTORS.md) file is updated weekly, with all-time contributors to the repository + * a [`CONTRIBUTORS.md`][contributors-doc] 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) @@ -101,7 +103,7 @@ This allows for minimal test dependencies. There are 3 complementary scanners - obviously, there is some overlap, but each has a different focus. -* github `CodeQL` +* GitHub `CodeQL` * `trivy` * `govulnscan` @@ -115,45 +117,70 @@ Reports are centralized in github security reports for code scanning tools. ## Releases +**For single module repos:** + +A bump release workflow can be triggered from the github actions UI to cut a release with a few clicks. + 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) +* configuration: the `.cliff.toml` is defined as a share configuration on + remote repo [`ci-workflows/.cliff.toml`][remote-cliff-config] + +Commits from maintainers are preferably PGP-signed. Tags are preferably PGP-signed. +We want our releases to show as "verified" on github. + 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 +**For mono-repos with multiple modules:** -Standard documentation: +The release process is slightly different because we need to update cross-module dependencies +before pushing a tag. -* [`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 - +A bump release workflow (mono-repo) can be triggered from the github actions UI to cut a release with a few clicks. -Reference documentation (released): +It works with the same input as the one for single module repos, and first creates a PR (auto-merged) +that updates the different go.mod files _before_ pushing the desired git tag. + +Commits and tags pushed by the workflow bot are PGP-signed ("go-openapi[bot]"). + +## Other files + +Standard documentation: -* [godoc](https://pkg.go.dev/github.com/go-openapi/spec) +* [CONTRIBUTING.md][contributing-doc] guidelines +* [DCO.md][dco-doc] terms for first-time contributors to read +* [CODE_OF_CONDUCT.md][coc-doc] +* [SECURITY.md][security-doc] policy: how to report vulnerabilities privately +* [LICENSE][license-doc] terms -## TODOs & other ideas + -A few things remain ahead to ease a bit a maintainer's job: +Reference documentation (released): -* [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 -* ... +* [pkg.go.dev (fka godoc)][godoc-url] + + +[linter-config]: https://github.com/go-openapi/spec/blob/master/.golangci.yml +[remote-cliff-config]: https://github.com/go-openapi/ci-workflows/blob/master/.cliff.toml +[dependabot-config]: https://github.com/go-openapi/spec/blob/master/.github/dependabot.yaml +[gocard-url]: https://goreportcard.com/report/github.com/go-openapi/spec +[codefactor-url]: https://www.codefactor.io/repository/github/go-openapi/spec +[golangci-url]: https://golangci-lint.run/ +[godoc-url]: https://pkg.go.dev/github.com/go-openapi/spec +[contributors-doc]: ../CONTRIBUTORS.md +[contributing-doc]: ../.github/CONTRIBUTING.md +[dco-doc]: ../.github/DCO.md +[style-doc]: STYLE.md +[coc-doc]: ../CODE_OF_CONDUCT.md +[security-doc]: ../SECURITY.md +[license-doc]: ../LICENSE + diff --git a/docs/STYLE.md b/docs/STYLE.md index 056fdb51..46f46cef 100644 --- a/docs/STYLE.md +++ b/docs/STYLE.md @@ -2,14 +2,14 @@ > **TL;DR** > -> Let's be honest: at `go-openapi` and `go-swagger` we've never been super-strict on code style etc. +> Let's be honest: at `go-openapi` and `go-swagger` we've never been super-strict on code style and linting. > > 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. +Now go-openapi and go-swagger together 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. @@ -21,8 +21,13 @@ 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. +> We use the `golangci-lint` meta-linter. The configuration lies in +> [`.golangci.yml`][golangci-yml]. +> You may read [the linter's configuration reference][golangci-doc] for additional reference. + +This configuration is essentially the same across all `go-openapi` projects. + +Some projects may require slightly different settings. ## Linting rules posture @@ -30,9 +35,32 @@ Thanks to go's original design, we developers don't have to waste much time argu However, the number of available linters has been growing to the point that we need to pick a choice. +### Our approach: evaluate, don't consume blindly + +As early adopters of `golangci-lint` (and its predecessors), we've watched linting orthodoxy +shift back and forth over the years. Patterns that were idiomatic one year get flagged the next; +rules that seemed reasonable in isolation produce noise at scale. Conversations with maintainers +of other large Go projects confirmed what our own experience taught us: +**the default linter set is a starting point, not a prescription**. + +Our stance is deliberate: + +- **Start from `default: all`**, then consciously disable what doesn't earn its keep. + This forces us to evaluate every linter and articulate why we reject it — the disabled list + is a design rationale, not technical debt. +- **Tune thresholds rather than disable** when a linter's principle is sound but its defaults + are too aggressive for a mature codebase. +- **Require justification for every `//nolint`** directive. Each one must carry an inline comment + explaining why it's there. +- **Prefer disabling a linter over scattering `//nolint`** across the codebase. If a linter + produces systematic false positives on patterns we use intentionally, the linter goes — + not our code. +- **Keep the configuration consistent** across all `go-openapi` repositories. Per-repo + divergence is a maintenance tax we don't want to pay. + 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`): +Here are the reasons why they are disabled (update: Feb. 2026, `golangci-lint v2.8.0`). ```yaml disable: @@ -46,6 +74,7 @@ Here are the reasons why they are disabled (update: Nov. 2025, `golangci-lint v2 - 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 + - thelper # too many false positives on test case factories returning func(*testing.T). See note below - 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 @@ -55,9 +84,11 @@ Here are the reasons why they are disabled (update: Nov. 2025, `golangci-lint v2 ``` 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. +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: +**Relaxed linter settings** + +When this is possible, we enable linters with relaxed constraints. ```yaml settings: @@ -81,3 +112,6 @@ When this is possible, we enable linters with relaxed constraints: Final note: since we have switched to a forked version of `stretchr/testify`, we no longer benefit from the great `testifylint` linter for tests. + +[golangci-yml]: https://github.com/go-openapi/spec/blob/master/.golangci.yml +[golangci-doc]: https://golangci-lint.run/docs/linters/configuration/ diff --git a/errors.go b/errors.go index e39ab8bf..eaca01cc 100644 --- a/errors.go +++ b/errors.go @@ -5,21 +5,21 @@ package spec import "errors" -// Error codes +// 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 is an error raised by the spec package. ErrSpec = errors.New("spec error") ) diff --git a/expander.go b/expander.go index ff45350a..f9c2fa32 100644 --- a/expander.go +++ b/expander.go @@ -38,7 +38,7 @@ func optionsOrDefault(opts *ExpandOptions) *ExpandOptions { return &ExpandOptions{} } -// ExpandSpec expands the references in a swagger spec +// ExpandSpec expands the references in a swagger spec. func ExpandSpec(spec *Swagger, options *ExpandOptions) error { options = optionsOrDefault(options) resolver := defaultSchemaLoader(spec, options, nil, nil) @@ -92,7 +92,7 @@ func ExpandSpec(spec *Swagger, options *ExpandOptions) error { const rootBase = ".root" // baseForRoot loads in the cache the root document and produces a fake ".root" base path entry -// for further $ref resolution +// for further $ref resolution. func baseForRoot(root any, cache ResolutionCache) string { // cache the root document to resolve $ref's normalizedBase := normalizeBase(rootBase) @@ -190,6 +190,7 @@ func expandItems(target Schema, parentRefs []string, resolver *schemaLoader, bas return &target, nil } +//nolint:gocognit,gocyclo,cyclop // complex but well-tested $ref expansion logic; refactoring deferred to dedicated PR func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) { if target.Ref.String() == "" && target.Ref.IsRoot() { newRef := normalizeRef(&target.Ref, basePath) @@ -464,7 +465,7 @@ func ExpandResponseWithRoot(response *Response, root any, cache ResolutionCache) // ExpandResponse expands a response based on a basepath // -// All refs inside response will be resolved relative to basePath +// All refs inside response will be resolved relative to basePath. func ExpandResponse(response *Response, basePath string) error { opts := optionsOrDefault(&ExpandOptions{ RelativeBase: basePath, @@ -491,7 +492,7 @@ func ExpandParameterWithRoot(parameter *Parameter, root any, cache ResolutionCac // ExpandParameter expands a parameter based on a basepath. // This is the exported version of expandParameter -// all refs inside parameter will be resolved relative to basePath +// all refs inside parameter will be resolved relative to basePath. func ExpandParameter(parameter *Parameter, basePath string) error { opts := optionsOrDefault(&ExpandOptions{ RelativeBase: basePath, @@ -565,7 +566,7 @@ func expandParameterOrResponse(input any, resolver *schemaLoader, basePath strin return nil } - if sch.Ref.String() != "" { + if sch.Ref.String() != "" { //nolint:nestif // intertwined ref rebasing and circularity check rebasedRef, ern := NewRef(normalizeURI(sch.Ref.String(), basePath)) if ern != nil { return ern diff --git a/expander_test.go b/expander_test.go index 1fc2e5b7..f7be753b 100644 --- a/expander_test.go +++ b/expander_test.go @@ -29,13 +29,13 @@ const ( //nolint:gochecknoglobals // it's okay to have embedded test fixtures as globals var ( - //go:embed fixtures/*/*.json fixtures/*/*.yaml fixtures/*/*.yml + //go:embed all:fixtures fixtureAssets embed.FS // PetStore20 json doc for swagger 2.0 pet store. PetStore20 []byte - // PetStoreJSONMessage json raw message for Petstore20 + // PetStoreJSONMessage json raw message for Petstore20. PetStoreJSONMessage json.RawMessage expectedExtraRef []byte expectedPathItem []byte @@ -73,17 +73,17 @@ func TestExpand_Issue148(t *testing.T) { return func(t *testing.T) { require.Len(t, sp.Definitions, 2) - require.Contains(t, sp.Definitions, "empty") + require.MapContainsT(t, sp.Definitions, "empty") empty := sp.Definitions["empty"] require.NotNil(t, empty.AdditionalProperties) require.NotNil(t, empty.AdditionalProperties.Schema) - require.True(t, empty.AdditionalProperties.Allows) + require.TrueT(t, empty.AdditionalProperties.Allows) - require.Contains(t, sp.Definitions, "false") + require.MapContainsT(t, sp.Definitions, "false") additionalIsFalse := sp.Definitions["false"] require.NotNil(t, additionalIsFalse.AdditionalProperties) require.Nil(t, additionalIsFalse.AdditionalProperties.Schema) - require.False(t, additionalIsFalse.AdditionalProperties.Allows) + require.FalseT(t, additionalIsFalse.AdditionalProperties.Allows) } } @@ -100,7 +100,7 @@ func TestExpand_KnownRef(t *testing.T) { schema := RefProperty("http://json-schema.org/draft-04/schema#") require.NoError(t, ExpandSchema(schema, nil, nil)) - assert.Equal(t, "Core schema meta-schema", schema.Description) + assert.EqualT(t, "Core schema meta-schema", schema.Description) // from the expanded schema, verify that all remaining $ref actually resolve jazon := asJSON(t, schema) @@ -126,7 +126,7 @@ func TestExpand_ResponseSchema(t *testing.T) { require.NotNil(t, sch) assert.Empty(t, sch.Ref.String()) - assert.Contains(t, sch.Type, "object") + assert.SliceContainsT(t, sch.Type, "object") assert.Len(t, sch.Properties, 2) } @@ -151,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) @@ -214,7 +213,7 @@ func TestExpand_InternalResponse(t *testing.T) { jazon := asJSON(t, expectedPet) - assert.JSONEq(t, `{ + assert.JSONEqT(t, `{ "description": "pet response", "schema": { "required": [ @@ -253,7 +252,7 @@ func TestExpand_InternalResponse(t *testing.T) { jazon = asJSON(t, successResponse) - assert.JSONEq(t, `{ + assert.JSONEqT(t, `{ "$ref": "#/responses/anotherPet" }`, jazon) @@ -467,8 +466,8 @@ func TestExpand_InternalSchemas2(t *testing.T) { require.NotNil(t, s) schema = *s - assert.Empty(t, schema.Items.Schema.Ref.String()) // no more a $ref - assert.False(t, schema.Items.Schema.Ref.IsRoot()) // no more a $ref + assert.Empty(t, schema.Items.Schema.Ref.String()) // no more a $ref + assert.FalseT(t, schema.Items.Schema.Ref.IsRoot()) // no more a $ref assert.Equal(t, spec.Definitions["car"], *schema.Items.Schema) sch := new(Schema) @@ -750,8 +749,7 @@ func TestExpand_InternalSchemas1(t *testing.T) { } func TestExpand_RelativeBaseURI(t *testing.T) { - server := httptest.NewServer(http.FileServer(http.Dir("fixtures/remote"))) - defer server.Close() + server := fixtureServer(t, "fixtures/remote") spec := new(Swagger) @@ -802,7 +800,7 @@ func TestExpand_RelativeBaseURI(t *testing.T) { // backRef navigates back to the root document (relative $ref) backRef := spec.Responses["backRef"] require.NoError(t, ExpandResponse(&backRef, opts.RelativeBase)) - assert.Equal(t, "pet response", backRef.Description) + assert.EqualT(t, "pet response", backRef.Description) assert.NotEmpty(t, backRef.Schema) assert.Empty(t, backRef.Ref) @@ -918,7 +916,7 @@ func TestExpandRemoteRef_WithNestedResolutionContext(t *testing.T) { require.Empty(t, tgt.Ref) require.NotNil(t, tgt.Items) require.NotNil(t, tgt.Schema) - assert.Equal(t, "deeper/", tgt.ID) // schema id is preserved + assert.EqualT(t, "deeper/", tgt.ID) // schema id is preserved assert.Equal(t, StringOrArray([]string{"string"}), tgt.Items.Schema.Type) assert.Empty(t, tgt.Items.Schema.Ref) @@ -949,7 +947,7 @@ func TestExpand_RemoteRefWithNestedResolutionContextWithFragment(t *testing.T) { require.Empty(t, tgt.Ref) require.NotNil(t, tgt.Items) require.NotNil(t, tgt.Schema) - assert.Equal(t, "deeper/", tgt.ID) // schema id is preserved + assert.EqualT(t, "deeper/", tgt.ID) // schema id is preserved assert.Equal(t, StringOrArray([]string{"file"}), tgt.Items.Schema.Type) assert.Empty(t, tgt.Items.Schema.Ref) @@ -970,7 +968,7 @@ func TestExpand_TransitiveRefs(t *testing.T) { require.NoError(t, ExpandSpec(spec, opts)) - assert.Equal(t, "todos.stoplight.io", spec.Host) // i.e. not empty + assert.EqualT(t, "todos.stoplight.io", spec.Host) // i.e. not empty jazon := asJSON(t, spec) // verify that the spec has been fully expanded @@ -1030,12 +1028,12 @@ func expandRootWithID(t testing.TB, root *Swagger, testcase string) { func TestExpand_PathItem(t *testing.T) { jazon, _ := expandThisOrDieTrying(t, pathItemsFixture) - assert.JSONEq(t, string(expectedPathItem), jazon) + assert.JSONEqT(t, string(expectedPathItem), jazon) } func TestExpand_ExtraItems(t *testing.T) { jazon, _ := expandThisOrDieTrying(t, extraRefFixture) - assert.JSONEq(t, string(expectedExtraRef), jazon) + assert.JSONEqT(t, string(expectedExtraRef), jazon) } func TestExpand_Issue145(t *testing.T) { @@ -1046,39 +1044,39 @@ func TestExpand_Issue145(t *testing.T) { // assert the internal behavior of baseForRoot() t.Run("with nil root, empty cache", func(t *testing.T) { cache := defaultResolutionCache() - require.Equal(t, pseudoRoot, baseForRoot(nil, cache)) + require.EqualT(t, pseudoRoot, baseForRoot(nil, cache)) t.Run("empty root is cached", func(t *testing.T) { value, ok := cache.Get(pseudoRoot) - require.True(t, ok) // found in cache + require.TrueT(t, ok) // found in cache asMap, ok := value.(map[string]any) - require.True(t, ok) + require.TrueT(t, ok) require.Empty(t, asMap) }) }) t.Run("with non-nil root, empty cache", func(t *testing.T) { cache := defaultResolutionCache() - require.Equal(t, pseudoRoot, baseForRoot(map[string]any{"key": "arbitrary"}, cache)) + require.EqualT(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 + require.TrueT(t, ok) // found in cache asMap, ok := value.(map[string]any) - require.True(t, ok) - require.Contains(t, asMap, "key") + require.TrueT(t, ok) + require.MapContainsT(t, asMap, "key") require.Equal(t, "arbitrary", asMap["key"]) }) t.Run("with nil root, non-empty cache", func(t *testing.T) { - require.Equal(t, pseudoRoot, baseForRoot(nil, cache)) + require.EqualT(t, pseudoRoot, baseForRoot(nil, cache)) t.Run("non-empty root is kept", func(t *testing.T) { value, ok := cache.Get(pseudoRoot) - require.True(t, ok) // found in cache + require.TrueT(t, ok) // found in cache asMap, ok := value.(map[string]any) - require.True(t, ok) - require.Contains(t, asMap, "key") + require.TrueT(t, ok) + require.MapContainsT(t, asMap, "key") require.Equal(t, "arbitrary", asMap["key"]) }) }) diff --git a/external_docs_test.go b/external_docs_test.go index 54b1e10c..3e8302f5 100644 --- a/external_docs_test.go +++ b/external_docs_test.go @@ -5,14 +5,17 @@ package spec import ( "testing" + + _ "github.com/go-openapi/testify/enable/yaml/v2" + "github.com/go-openapi/testify/v2/assert" ) 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) - assertSerializeYAML(t, extDocs, extDocsYAML) - assertParsesJSON(t, extDocsJSON, extDocs) - assertParsesYAML(t, extDocsYAML, extDocs) + assert.JSONMarshalAsT(t, extDocsJSON, extDocs) + assert.YAMLMarshalAsT(t, extDocsYAML, extDocs) + assert.JSONUnmarshalAsT(t, extDocs, extDocsJSON) + assert.YAMLUnmarshalAsT(t, extDocs, extDocsYAML) } diff --git a/go.mod b/go.mod index d92cc780..b1ed3850 100644 --- a/go.mod +++ b/go.mod @@ -1,20 +1,21 @@ module github.com/go-openapi/spec require ( - 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/jsonpointer v0.22.5 + github.com/go-openapi/jsonreference v0.21.5 + github.com/go-openapi/swag/conv v0.25.5 + github.com/go-openapi/swag/jsonname v0.25.5 + github.com/go-openapi/swag/jsonutils v0.25.5 + github.com/go-openapi/swag/loading v0.25.5 + github.com/go-openapi/swag/stringutils v0.25.5 + github.com/go-openapi/testify/enable/yaml/v2 v2.4.0 github.com/go-openapi/testify/v2 v2.4.0 - go.yaml.in/yaml/v3 v3.0.4 ) require ( - github.com/go-openapi/swag/typeutils v0.25.4 // indirect - github.com/go-openapi/swag/yamlutils v0.25.4 // indirect + github.com/go-openapi/swag/typeutils v0.25.5 // indirect + github.com/go-openapi/swag/yamlutils v0.25.5 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect ) go 1.24.0 diff --git a/go.sum b/go.sum index f9e18b33..39d1d0b3 100644 --- a/go.sum +++ b/go.sum @@ -1,25 +1,25 @@ -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/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA= +github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0= +github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE= +github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw= +github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g= +github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k= +github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo= +github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU= +github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo= +github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo= +github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU= +github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g= +github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M= +github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII= +github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E= +github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc= +github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ= +github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ= +github.com/go-openapi/testify/enable/yaml/v2 v2.4.0 h1:7SgOMTvJkM8yWrQlU8Jm18VeDPuAvB/xWrdxFJkoFag= +github.com/go-openapi/testify/enable/yaml/v2 v2.4.0/go.mod h1:14iV8jyyQlinc9StD7w1xVPW3CO3q1Gj04Jy//Kw4VM= github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM= github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= diff --git a/header.go b/header.go index ab251ef7..599ba2c5 100644 --- a/header.go +++ b/header.go @@ -15,7 +15,7 @@ const ( jsonArray = "array" ) -// HeaderProps describes a response header +// HeaderProps describes a response header. type HeaderProps struct { Description string `json:"description,omitempty"` } @@ -30,25 +30,25 @@ type Header struct { HeaderProps } -// ResponseHeader creates a new header instance for use in a response +// ResponseHeader creates a new header instance for use in a response. func ResponseHeader() *Header { return new(Header) } -// WithDescription sets the description on this response, allows for chaining +// WithDescription sets the description on this response, allows for chaining. func (h *Header) WithDescription(description string) *Header { h.Description = description return h } -// Typed a fluent builder method for the type of parameter +// Typed a fluent builder method for the type of parameter. func (h *Header) Typed(tpe, format string) *Header { h.Type = tpe h.Format = format return h } -// CollectionOf a fluent builder method for an array item +// CollectionOf a fluent builder method for an array item. func (h *Header) CollectionOf(items *Items, format string) *Header { h.Type = jsonArray h.Items = items @@ -56,87 +56,87 @@ func (h *Header) CollectionOf(items *Items, format string) *Header { return h } -// WithDefault sets the default value on this item +// WithDefault sets the default value on this item. func (h *Header) WithDefault(defaultValue any) *Header { h.Default = defaultValue return h } -// WithMaxLength sets a max length value +// WithMaxLength sets a max length value. func (h *Header) WithMaxLength(maximum int64) *Header { h.MaxLength = &maximum return h } -// WithMinLength sets a min length value +// WithMinLength sets a min length value. func (h *Header) WithMinLength(minimum int64) *Header { h.MinLength = &minimum return h } -// WithPattern sets a pattern value +// WithPattern sets a pattern value. func (h *Header) WithPattern(pattern string) *Header { h.Pattern = pattern return h } -// WithMultipleOf sets a multiple of value +// WithMultipleOf sets a multiple of value. func (h *Header) WithMultipleOf(number float64) *Header { h.MultipleOf = &number return h } -// WithMaximum sets a maximum number value +// WithMaximum sets a maximum number value. func (h *Header) WithMaximum(maximum float64, exclusive bool) *Header { h.Maximum = &maximum h.ExclusiveMaximum = exclusive return h } -// WithMinimum sets a minimum number value +// WithMinimum sets a minimum number value. func (h *Header) WithMinimum(minimum float64, exclusive bool) *Header { h.Minimum = &minimum h.ExclusiveMinimum = exclusive return h } -// WithEnum sets a the enum values (replace) +// WithEnum sets a the enum values (replace). func (h *Header) WithEnum(values ...any) *Header { h.Enum = append([]any{}, values...) return h } -// WithMaxItems sets the max items +// WithMaxItems sets the max items. func (h *Header) WithMaxItems(size int64) *Header { h.MaxItems = &size return h } -// WithMinItems sets the min items +// WithMinItems sets the min items. func (h *Header) WithMinItems(size int64) *Header { h.MinItems = &size return h } -// UniqueValues dictates that this array can only have unique items +// UniqueValues dictates that this array can only have unique items. func (h *Header) UniqueValues() *Header { h.UniqueItems = true return h } -// AllowDuplicates this array can have duplicates +// AllowDuplicates this array can have duplicates. func (h *Header) AllowDuplicates() *Header { h.UniqueItems = false return h } -// WithValidations is a fluent method to set header validations +// WithValidations is a fluent method to set header validations. func (h *Header) WithValidations(val CommonValidations) *Header { h.SetValidations(SchemaValidations{CommonValidations: val}) return h } -// MarshalJSON marshal this to JSON +// MarshalJSON marshal this to JSON. func (h Header) MarshalJSON() ([]byte, error) { b1, err := json.Marshal(h.CommonValidations) if err != nil { @@ -153,7 +153,7 @@ func (h Header) MarshalJSON() ([]byte, error) { return jsonutils.ConcatJSON(b1, b2, b3), nil } -// UnmarshalJSON unmarshals this header from JSON +// UnmarshalJSON unmarshals this header from JSON. func (h *Header) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &h.CommonValidations); err != nil { return err @@ -167,7 +167,7 @@ func (h *Header) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &h.HeaderProps) } -// JSONLookup look up a value by the json property name +// JSONLookup look up a value by the json property name. 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 e4a27f70..ad34da3e 100644 --- a/header_test.go +++ b/header_test.go @@ -4,7 +4,6 @@ package spec import ( - "encoding/json" "testing" "github.com/go-openapi/swag/conv" @@ -17,11 +16,12 @@ const epsilon = 1e-9 func float64Ptr(f float64) *float64 { return &f } + func int64Ptr(f int64) *int64 { return &f } -var header = Header{ +var header = Header{ //nolint:gochecknoglobals // test fixture VendorExtensible: VendorExtensible{Extensions: map[string]any{ "x-framework": "swagger-go", }}, @@ -74,11 +74,7 @@ const headerJSON = `{ }` func TestIntegrationHeader(t *testing.T) { - var actual Header - require.NoError(t, json.Unmarshal([]byte(headerJSON), &actual)) - assert.Equal(t, actual, header) - - assertParsesJSON(t, headerJSON, header) + assert.JSONUnmarshalAsT(t, header, headerJSON) } func TestJSONLookupHeader(t *testing.T) { @@ -90,8 +86,8 @@ func TestJSONLookupHeader(t *testing.T) { var ok bool def, ok = res.(string) - require.True(t, ok) - assert.Equal(t, "8", def) + require.TrueT(t, ok) + assert.EqualT(t, "8", def) var x *any res, err = header.JSONLookup("x-framework") @@ -100,7 +96,7 @@ func TestJSONLookupHeader(t *testing.T) { require.IsType(t, x, res) x, ok = res.(*any) - require.True(t, ok) + require.TrueT(t, ok) assert.EqualValues(t, "swagger-go", *x) res, err = header.JSONLookup("unknown") @@ -114,8 +110,8 @@ func TestJSONLookupHeader(t *testing.T) { require.IsType(t, maximum, res) maximum, ok = res.(*float64) - require.True(t, ok) - assert.InDelta(t, float64(100), *maximum, epsilon) + require.TrueT(t, ok) + assert.InDeltaT(t, float64(100), *maximum, epsilon) } func TestResponseHeaueder(t *testing.T) { @@ -126,20 +122,20 @@ func TestResponseHeaueder(t *testing.T) { func TestWithHeader(t *testing.T) { h := new(Header).WithDescription("header description").Typed("integer", "int32") - assert.Equal(t, "header description", h.Description) - assert.Equal(t, "integer", h.Type) - assert.Equal(t, "int32", h.Format) + assert.EqualT(t, "header description", h.Description) + assert.EqualT(t, "integer", h.Type) + assert.EqualT(t, "int32", h.Format) i := new(Items).Typed("string", "date") h = new(Header).CollectionOf(i, "pipe") assert.Equal(t, *i, *h.Items) - assert.Equal(t, "pipe", h.CollectionFormat) + assert.EqualT(t, "pipe", h.CollectionFormat) h = new(Header).WithDefault([]string{"a", "b", "c"}).WithMaxLength(10).WithMinLength(3) - assert.Equal(t, int64(10), *h.MaxLength) - assert.Equal(t, int64(3), *h.MinLength) + assert.EqualT(t, int64(10), *h.MaxLength) + assert.EqualT(t, int64(3), *h.MinLength) assert.EqualValues(t, []string{"a", "b", "c"}, h.Default) h = new(Header).WithPattern("^abc$") diff --git a/helpers_spec_test.go b/helpers_spec_test.go index a2c542e6..7d5374f6 100644 --- a/helpers_spec_test.go +++ b/helpers_spec_test.go @@ -18,10 +18,10 @@ import ( var ( rex = regexp.MustCompile(`"\$ref":\s*"(.*?)"`) - testLoader func(string) (json.RawMessage, error) + testLoader func(string) (json.RawMessage, error) //nolint:gochecknoglobals // test fixture ) -func init() { +func init() { //nolint:gochecknoinits // sets up test loader for spec loading fixtures // mimics what the go-openapi/load does testLoader = func(path string) (json.RawMessage, error) { if loading.YAMLMatcher(path) { @@ -51,7 +51,7 @@ func assertRefInJSON(t testing.TB, jazon, prefix string) { for _, matched := range m { subMatch := matched[1] - assert.True(t, strings.HasPrefix(subMatch, prefix), + assert.TrueT(t, strings.HasPrefix(subMatch, prefix), "expected $ref to match %q, got: %s", prefix, matched[0]) } } @@ -66,7 +66,7 @@ func assertRefInJSONRegexp(t testing.TB, jazon, match string) { for _, matched := range m { subMatch := matched[1] - assert.True(t, refMatch.MatchString(subMatch), + assert.TrueT(t, refMatch.MatchString(subMatch), "expected $ref to match %q, got: %s", match, matched[0]) } } @@ -146,7 +146,7 @@ func asJSON(t testing.TB, sp any) string { return string(bbb) } -// assertNoRef ensures that no $ref is remaining in json doc +// assertNoRef ensures that no $ref is remaining in json doc. func assertNoRef(t testing.TB, jazon string) { m := rex.FindAllStringSubmatch(jazon, -1) require.Nil(t, m) diff --git a/helpers_test.go b/helpers_test.go index 6eab8ea5..06ab941d 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -6,6 +6,10 @@ package spec import ( "encoding/json" "fmt" + "io/fs" + "net/http" + "net/http/httptest" + "path/filepath" "regexp" "strings" "testing" @@ -17,6 +21,22 @@ import ( var rex = regexp.MustCompile(`"\$ref":\s*"(.*?)"`) +// fixtureServer returns an httptest.Server serving the given subdirectory +// from the embedded fixtureAssets FS. This avoids OS-level file serving +// (and the Windows TransmitFile/sendfile code path that has a data race +// in Go 1.26). +func fixtureServer(t testing.TB, dir string) *httptest.Server { + t.Helper() + + sub, err := fs.Sub(fixtureAssets, filepath.ToSlash(dir)) + require.NoError(t, err) + + server := httptest.NewServer(http.FileServerFS(sub)) + t.Cleanup(server.Close) + + return server +} + func jsonDoc(path string) (json.RawMessage, error) { data, err := loading.LoadFromFileOrHTTP(path) if err != nil { @@ -76,7 +96,7 @@ func assertRefInJSON(t testing.TB, jazon, prefix string) { for _, matched := range m { subMatch := matched[1] - assert.True(t, strings.HasPrefix(subMatch, prefix), + assert.TrueT(t, strings.HasPrefix(subMatch, prefix), "expected $ref to match %q, got: %s", prefix, matched[0]) } } @@ -94,12 +114,12 @@ func assertRefInJSONRegexp(t testing.TB, jazon, match string) { for _, matched := range m { subMatch := matched[1] - assert.True(t, refMatch.MatchString(subMatch), + assert.TrueT(t, refMatch.MatchString(subMatch), "expected $ref to match %q, got: %s", match, matched[0]) } } -// assertNoRef ensures that no $ref is remaining in json doc +// assertNoRef ensures that no $ref is remaining in json doc. func assertNoRef(t testing.TB, jazon string) { m := rex.FindAllStringSubmatch(jazon, -1) require.Nil(t, m) diff --git a/info.go b/info.go index 9401065b..0ccfdccc 100644 --- a/info.go +++ b/info.go @@ -12,16 +12,16 @@ import ( "github.com/go-openapi/swag/jsonutils" ) -// Extensions vendor specific extensions +// Extensions vendor specific extensions. type Extensions map[string]any -// Add adds a value to these extensions +// Add adds a value to these extensions. func (e Extensions) Add(key string, value any) { realKey := strings.ToLower(key) e[realKey] = value } -// GetString gets a string value from the extensions +// GetString gets a string value from the extensions. func (e Extensions) GetString(key string) (string, bool) { if v, ok := e[strings.ToLower(key)]; ok { str, ok := v.(string) @@ -30,7 +30,7 @@ func (e Extensions) GetString(key string) (string, bool) { return "", false } -// GetInt gets a int value from the extensions +// GetInt gets a int value from the extensions. func (e Extensions) GetInt(key string) (int, bool) { realKey := strings.ToLower(key) @@ -48,7 +48,7 @@ func (e Extensions) GetInt(key string) (int, bool) { return -1, false } -// GetBool gets a string value from the extensions +// GetBool gets a string value from the extensions. func (e Extensions) GetBool(key string) (bool, bool) { if v, ok := e[strings.ToLower(key)]; ok { str, ok := v.(bool) @@ -57,7 +57,7 @@ func (e Extensions) GetBool(key string) (bool, bool) { return false, false } -// GetStringSlice gets a string value from the extensions +// 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.([]any) @@ -82,7 +82,7 @@ type VendorExtensible struct { Extensions Extensions } -// AddExtension adds an extension to this extensible object +// AddExtension adds an extension to this extensible object. func (v *VendorExtensible) AddExtension(key string, value any) { if value == nil { return @@ -93,7 +93,7 @@ func (v *VendorExtensible) AddExtension(key string, value any) { v.Extensions.Add(key, value) } -// MarshalJSON marshals the extensions to json +// MarshalJSON marshals the extensions to json. func (v VendorExtensible) MarshalJSON() ([]byte, error) { toser := make(map[string]any) for k, v := range v.Extensions { @@ -105,7 +105,7 @@ func (v VendorExtensible) MarshalJSON() ([]byte, error) { return json.Marshal(toser) } -// UnmarshalJSON for this extensible object +// UnmarshalJSON for this extensible object. func (v *VendorExtensible) UnmarshalJSON(data []byte) error { var d map[string]any if err := json.Unmarshal(data, &d); err != nil { @@ -123,7 +123,7 @@ func (v *VendorExtensible) UnmarshalJSON(data []byte) error { return nil } -// InfoProps the properties for an info definition +// InfoProps the properties for an info definition. type InfoProps struct { Description string `json:"description,omitempty"` Title string `json:"title,omitempty"` @@ -142,7 +142,7 @@ type Info struct { InfoProps } -// JSONLookup look up a value by the json property name +// JSONLookup look up a value by the json property name. func (i Info) JSONLookup(token string) (any, error) { if ex, ok := i.Extensions[token]; ok { return &ex, nil @@ -151,7 +151,7 @@ func (i Info) JSONLookup(token string) (any, error) { return r, err } -// MarshalJSON marshal this to JSON +// MarshalJSON marshal this to JSON. func (i Info) MarshalJSON() ([]byte, error) { b1, err := json.Marshal(i.InfoProps) if err != nil { @@ -164,7 +164,7 @@ func (i Info) MarshalJSON() ([]byte, error) { return jsonutils.ConcatJSON(b1, b2), nil } -// UnmarshalJSON marshal this from JSON +// UnmarshalJSON marshal this from JSON. func (i *Info) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &i.InfoProps); err != nil { return err diff --git a/info_test.go b/info_test.go index 6185d641..8ebace52 100644 --- a/info_test.go +++ b/info_test.go @@ -28,7 +28,7 @@ const infoJSON = `{ "x-framework": "go-swagger" }` -var testInfo = Info{ +var testInfo = Info{ //nolint:gochecknoglobals // test fixture InfoProps: InfoProps{ Version: "1.0.9-abcd", Title: "Swagger Sample API", @@ -36,10 +36,11 @@ var testInfo = 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]any{"x-framework": "go-swagger"}}, @@ -47,15 +48,11 @@ var testInfo = Info{ 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)) + assert.JSONMarshalAsT(t, infoJSON, testInfo) }) t.Run("should unmarshal Info", func(t *testing.T) { - actual := Info{} - require.NoError(t, json.Unmarshal([]byte(infoJSON), &actual)) - assert.Equal(t, testInfo, actual) + assert.JSONUnmarshalAsT(t, testInfo, infoJSON) }) t.Run("should GobEncode Info", func(t *testing.T) { diff --git a/items.go b/items.go index d30ca356..daf5a4fd 100644 --- a/items.go +++ b/items.go @@ -15,7 +15,7 @@ const ( jsonRef = "$ref" ) -// SimpleSchema describe swagger simple schemas for parameters and headers +// SimpleSchema describe swagger simple schemas for parameters and headers. type SimpleSchema struct { Type string `json:"type,omitempty"` Nullable bool `json:"nullable,omitempty"` @@ -26,7 +26,7 @@ type SimpleSchema struct { Example any `json:"example,omitempty"` } -// TypeName return the type (or format) of a simple schema +// TypeName return the type (or format) of a simple schema. func (s *SimpleSchema) TypeName() string { if s.Format != "" { return s.Format @@ -34,7 +34,7 @@ func (s *SimpleSchema) TypeName() string { return s.Type } -// ItemsTypeName yields the type of items in a simple schema array +// ItemsTypeName yields the type of items in a simple schema array. func (s *SimpleSchema) ItemsTypeName() string { if s.Items == nil { return "" @@ -53,12 +53,12 @@ type Items struct { VendorExtensible } -// NewItems creates a new instance of items +// NewItems creates a new instance of items. func NewItems() *Items { return &Items{} } -// Typed a fluent builder method for the type of item +// Typed a fluent builder method for the type of item. func (i *Items) Typed(tpe, format string) *Items { i.Type = tpe i.Format = format @@ -71,7 +71,7 @@ func (i *Items) AsNullable() *Items { return i } -// CollectionOf a fluent builder method for an array item +// CollectionOf a fluent builder method for an array item. func (i *Items) CollectionOf(items *Items, format string) *Items { i.Type = jsonArray i.Items = items @@ -79,87 +79,87 @@ func (i *Items) CollectionOf(items *Items, format string) *Items { return i } -// WithDefault sets the default value on this item +// WithDefault sets the default value on this item. func (i *Items) WithDefault(defaultValue any) *Items { i.Default = defaultValue return i } -// WithMaxLength sets a max length value +// WithMaxLength sets a max length value. func (i *Items) WithMaxLength(maximum int64) *Items { i.MaxLength = &maximum return i } -// WithMinLength sets a min length value +// WithMinLength sets a min length value. func (i *Items) WithMinLength(minimum int64) *Items { i.MinLength = &minimum return i } -// WithPattern sets a pattern value +// WithPattern sets a pattern value. func (i *Items) WithPattern(pattern string) *Items { i.Pattern = pattern return i } -// WithMultipleOf sets a multiple of value +// WithMultipleOf sets a multiple of value. func (i *Items) WithMultipleOf(number float64) *Items { i.MultipleOf = &number return i } -// WithMaximum sets a maximum number value +// WithMaximum sets a maximum number value. func (i *Items) WithMaximum(maximum float64, exclusive bool) *Items { i.Maximum = &maximum i.ExclusiveMaximum = exclusive return i } -// WithMinimum sets a minimum number value +// WithMinimum sets a minimum number value. func (i *Items) WithMinimum(minimum float64, exclusive bool) *Items { i.Minimum = &minimum i.ExclusiveMinimum = exclusive return i } -// WithEnum sets a the enum values (replace) +// WithEnum sets a the enum values (replace). func (i *Items) WithEnum(values ...any) *Items { i.Enum = append([]any{}, values...) return i } -// WithMaxItems sets the max items +// WithMaxItems sets the max items. func (i *Items) WithMaxItems(size int64) *Items { i.MaxItems = &size return i } -// WithMinItems sets the min items +// WithMinItems sets the min items. func (i *Items) WithMinItems(size int64) *Items { i.MinItems = &size return i } -// UniqueValues dictates that this array can only have unique items +// UniqueValues dictates that this array can only have unique items. func (i *Items) UniqueValues() *Items { i.UniqueItems = true return i } -// AllowDuplicates this array can have duplicates +// AllowDuplicates this array can have duplicates. func (i *Items) AllowDuplicates() *Items { i.UniqueItems = false return i } -// WithValidations is a fluent method to set Items validations +// WithValidations is a fluent method to set Items validations. func (i *Items) WithValidations(val CommonValidations) *Items { i.SetValidations(SchemaValidations{CommonValidations: val}) return i } -// UnmarshalJSON hydrates this items instance with the data from JSON +// UnmarshalJSON hydrates this items instance with the data from JSON. func (i *Items) UnmarshalJSON(data []byte) error { var validations CommonValidations if err := json.Unmarshal(data, &validations); err != nil { @@ -184,7 +184,7 @@ func (i *Items) UnmarshalJSON(data []byte) error { return nil } -// MarshalJSON converts this items object to JSON +// MarshalJSON converts this items object to JSON. func (i Items) MarshalJSON() ([]byte, error) { b1, err := json.Marshal(i.CommonValidations) if err != nil { @@ -205,7 +205,7 @@ func (i Items) MarshalJSON() ([]byte, error) { return jsonutils.ConcatJSON(b4, b3, b1, b2), nil } -// JSONLookup look up a value by the json property name +// JSONLookup look up a value by the json property name. 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 c5c585cd..e59d033b 100644 --- a/items_test.go +++ b/items_test.go @@ -4,7 +4,6 @@ package spec import ( - "encoding/json" "testing" "github.com/go-openapi/swag/conv" @@ -12,7 +11,7 @@ import ( "github.com/go-openapi/testify/v2/require" ) -var testItems = Items{ +var testItems = Items{ //nolint:gochecknoglobals // test fixture Refable: Refable{Ref: MustCreateRef("Dog")}, CommonValidations: CommonValidations{ Maximum: float64Ptr(100), @@ -63,18 +62,14 @@ const itemsJSON = `{ }` func TestIntegrationItems(t *testing.T) { - var actual Items - require.NoError(t, json.Unmarshal([]byte(itemsJSON), &actual)) - assert.Equal(t, actual, testItems) - - assertParsesJSON(t, itemsJSON, testItems) + assert.JSONUnmarshalAsT(t, testItems, itemsJSON) } func TestTypeNameItems(t *testing.T) { var nilItems Items assert.Empty(t, nilItems.TypeName()) - assert.Equal(t, "date", testItems.TypeName()) + assert.EqualT(t, "date", testItems.TypeName()) assert.Empty(t, testItems.ItemsTypeName()) nested := Items{ @@ -90,23 +85,23 @@ func TestTypeNameItems(t *testing.T) { }, } - assert.Equal(t, "array", nested.TypeName()) - assert.Equal(t, "int32", nested.ItemsTypeName()) + assert.EqualT(t, "array", nested.TypeName()) + assert.EqualT(t, "int32", nested.ItemsTypeName()) simple := SimpleSchema{ Type: "string", Items: nil, } - assert.Equal(t, "string", simple.TypeName()) + assert.EqualT(t, "string", simple.TypeName()) assert.Empty(t, simple.ItemsTypeName()) simple.Items = NewItems() simple.Type = "array" simple.Items.Type = "string" - assert.Equal(t, "array", simple.TypeName()) - assert.Equal(t, "string", simple.ItemsTypeName()) + assert.EqualT(t, "array", simple.TypeName()) + assert.EqualT(t, "string", simple.ItemsTypeName()) } func TestItemsBuilder(t *testing.T) { @@ -147,7 +142,7 @@ func TestJSONLookupItems(t *testing.T) { require.IsType(t, &Ref{}, res) ref, ok := res.(*Ref) - require.True(t, ok) + require.TrueT(t, ok) assert.Equal(t, MustCreateRef("Dog"), *ref) }) @@ -160,8 +155,8 @@ func TestJSONLookupItems(t *testing.T) { var ok bool maximum, ok = res.(*float64) - require.True(t, ok) - assert.InDelta(t, float64(100), *maximum, epsilon) + require.TrueT(t, ok) + assert.InDeltaT(t, float64(100), *maximum, epsilon) }) t.Run(`lookup should find "collectionFormat"`, func(t *testing.T) { @@ -172,8 +167,8 @@ func TestJSONLookupItems(t *testing.T) { require.IsType(t, f, res) f, ok := res.(string) - require.True(t, ok) - assert.Equal(t, "csv", f) + require.TrueT(t, ok) + assert.EqualT(t, "csv", f) }) t.Run(`lookup should fail on "unknown"`, func(t *testing.T) { diff --git a/license.go b/license.go index 286b237e..8209f218 100644 --- a/license.go +++ b/license.go @@ -17,13 +17,13 @@ type License struct { VendorExtensible } -// LicenseProps holds the properties of a License object +// LicenseProps holds the properties of a License object. type LicenseProps struct { Name string `json:"name,omitempty"` URL string `json:"url,omitempty"` } -// UnmarshalJSON hydrates License from json +// UnmarshalJSON hydrates License from json. func (l *License) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &l.LicenseProps); err != nil { return err @@ -31,7 +31,7 @@ func (l *License) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &l.VendorExtensible) } -// MarshalJSON produces License as json +// MarshalJSON produces License as json. func (l License) MarshalJSON() ([]byte, error) { b1, err := json.Marshal(l.LicenseProps) if err != nil { diff --git a/license_test.go b/license_test.go index 1a197cb8..56f75b65 100644 --- a/license_test.go +++ b/license_test.go @@ -4,11 +4,9 @@ package spec import ( - "encoding/json" "testing" "github.com/go-openapi/testify/v2/assert" - "github.com/go-openapi/testify/v2/require" ) func TestIntegrationLicense(t *testing.T) { @@ -18,22 +16,18 @@ func TestIntegrationLicense(t *testing.T) { "x-license": "custom term" }` - var testLicense = License{ + testLicense := License{ LicenseProps: LicenseProps{Name: "the name", URL: "the url"}, - VendorExtensible: VendorExtensible{Extensions: map[string]any{"x-license": "custom term"}}} + VendorExtensible: VendorExtensible{Extensions: map[string]any{"x-license": "custom term"}}, + } // const licenseYAML = "name: the name\nurl: the url\n" 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)) + assert.JSONMarshalAsT(t, licenseJSON, testLicense) }) 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) + assert.JSONUnmarshalAsT(t, testLicense, licenseJSON) }) } diff --git a/normalizer.go b/normalizer.go index e1d7c58d..68252dc3 100644 --- a/normalizer.go +++ b/normalizer.go @@ -138,7 +138,7 @@ func rebase(ref *Ref, v *url.URL, notEqual bool) (Ref, bool) { return MustCreateRef(newBase.String()), true } -// normalizeRef canonicalize a Ref, using a canonical relativeBase as its absolute anchor +// normalizeRef canonicalize a Ref, using a canonical relativeBase as its absolute anchor. func normalizeRef(ref *Ref, relativeBase string) *Ref { r := MustCreateRef(normalizeURI(ref.String(), relativeBase)) return &r diff --git a/normalizer_test.go b/normalizer_test.go index 48ab6bce..a7ab306c 100644 --- a/normalizer_test.go +++ b/normalizer_test.go @@ -17,10 +17,10 @@ import ( const windowsOS = "windows" -// only used for windows -var currentDriveLetter = getCurrentDrive() +// only used for windows. +var currentDriveLetter = getCurrentDrive() //nolint:gochecknoglobals // test fixture -// get the current drive letter in lowercase on windows that the test is running +// get the current drive letter in lowercase on windows that the test is running. func getCurrentDrive() string { if runtime.GOOS != windowsOS { return "" @@ -260,7 +260,7 @@ func TestNormalizer_NormalizeURI(t *testing.T) { t.Run(testCase.refPath, func(t *testing.T) { t.Parallel() out := normalizeURI(testCase.refPath, testCase.base) - assert.Equalf(t, testCase.expOutput, out, + assert.EqualTf(t, testCase.expOutput, out, "unexpected normalized URL with $ref %q and base %q", testCase.refPath, testCase.base) }) } @@ -295,7 +295,7 @@ func TestNormalizer_NormalizeBase(t *testing.T) { Base: ".", Expected: "file://$cwd", // edge case: this won't work because a document is a file }, - { + { //nolint:gosec // test data, not real credentials Base: "https://user:password@www.example.com:123/base/sub/file.json", Expected: "https://user:password@www.example.com:123/base/sub/file.json", }, @@ -466,10 +466,10 @@ func TestNormalizer_NormalizeBase(t *testing.T) { t.Run(testCase.Base, func(t *testing.T) { t.Parallel() expected := strings.ReplaceAll(strings.ReplaceAll(testCase.Expected, "$cwd", cwd), "$dir", path.Dir(cwd)) - require.Equalf(t, expected, normalizeBase(testCase.Base), "for base %q", testCase.Base) + require.EqualTf(t, expected, normalizeBase(testCase.Base), "for base %q", testCase.Base) // check for idempotence - require.Equalf(t, expected, normalizeBase(normalizeBase(testCase.Base)), + require.EqualTf(t, expected, normalizeBase(normalizeBase(testCase.Base)), "expected idempotent behavior on base %q", testCase.Base) }) } @@ -494,7 +494,7 @@ func TestNormalizer_Denormalize(t *testing.T) { Ref: "#/definitions/X", Expected: "#/definitions/X", }, - { + { //nolint:gosec // test data, not real credentials OriginalBase: "https://user:password@example.com/a/b/c/file.json", Ref: "https://user:password@example.com/a/b/c/other.json#/definitions/X", Expected: "other.json#/definitions/X", @@ -609,7 +609,7 @@ func TestNormalizer_Denormalize(t *testing.T) { ref := MustCreateRef(testCase.Ref) newRef := denormalizeRef(&ref, testCase.OriginalBase, testCase.ID) require.NotNil(t, newRef) - require.Equalf(t, expected, newRef.String(), + require.EqualTf(t, expected, newRef.String(), "expected %s, but got %s", testCase.Expected, newRef.String()) }) } diff --git a/operation.go b/operation.go index 29d9c4f4..cd70d254 100644 --- a/operation.go +++ b/operation.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/swag/jsonutils" ) -func init() { +func init() { //nolint:gochecknoinits // registers gob types for Operation serialization gob.Register(map[string]any{}) gob.Register([]any{}) } @@ -22,7 +22,7 @@ func init() { // // NOTES: // - schemes, when present must be from [http, https, ws, wss]: see validate -// - Security is handled as a special case: see MarshalJSON function +// - Security is handled as a special case: see MarshalJSON function. type OperationProps struct { Description string `json:"description,omitempty"` Consumes []string `json:"consumes,omitempty"` @@ -82,7 +82,7 @@ func NewOperation(id string) *Operation { return op } -// SuccessResponse gets a success response model +// SuccessResponse gets a success response model. func (o *Operation) SuccessResponse() (*Response, int, bool) { if o.Responses == nil { return nil, 0, false @@ -103,7 +103,7 @@ func (o *Operation) SuccessResponse() (*Response, int, bool) { return o.Responses.Default, 0, false } -// JSONLookup look up a value by the json property name +// JSONLookup look up a value by the json property name. func (o Operation) JSONLookup(token string) (any, error) { if ex, ok := o.Extensions[token]; ok { return &ex, nil @@ -112,7 +112,7 @@ func (o Operation) JSONLookup(token string) (any, error) { return r, err } -// UnmarshalJSON hydrates this items instance with the data from JSON +// UnmarshalJSON hydrates this items instance with the data from JSON. func (o *Operation) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &o.OperationProps); err != nil { return err @@ -120,7 +120,7 @@ func (o *Operation) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &o.VendorExtensible) } -// MarshalJSON converts this items object to JSON +// MarshalJSON converts this items object to JSON. func (o Operation) MarshalJSON() ([]byte, error) { b1, err := json.Marshal(o.OperationProps) if err != nil { @@ -140,13 +140,13 @@ func (o *Operation) WithID(id string) *Operation { return o } -// WithDescription sets the description on this operation, allows for chaining +// WithDescription sets the description on this operation, allows for chaining. func (o *Operation) WithDescription(description string) *Operation { o.Description = description return o } -// WithSummary sets the summary on this operation, allows for chaining +// WithSummary sets the summary on this operation, allows for chaining. func (o *Operation) WithSummary(summary string) *Operation { o.Summary = summary return o @@ -170,38 +170,38 @@ func (o *Operation) WithExternalDocs(description, url string) *Operation { return o } -// Deprecate marks the operation as deprecated +// Deprecate marks the operation as deprecated. func (o *Operation) Deprecate() *Operation { o.Deprecated = true return o } -// Undeprecate marks the operation as not deprecated +// Undeprecate marks the operation as not deprecated. func (o *Operation) Undeprecate() *Operation { o.Deprecated = false return o } -// WithConsumes adds media types for incoming body values +// WithConsumes adds media types for incoming body values. func (o *Operation) WithConsumes(mediaTypes ...string) *Operation { o.Consumes = append(o.Consumes, mediaTypes...) return o } -// WithProduces adds media types for outgoing body values +// WithProduces adds media types for outgoing body values. func (o *Operation) WithProduces(mediaTypes ...string) *Operation { o.Produces = append(o.Produces, mediaTypes...) return o } -// WithTags adds tags for this operation +// WithTags adds tags for this operation. func (o *Operation) WithTags(tags ...string) *Operation { o.Tags = append(o.Tags, tags...) return o } // AddParam adds a parameter to this operation, when a parameter for that location -// and with that name already exists it will be replaced +// and with that name already exists it will be replaced. func (o *Operation) AddParam(param *Parameter) *Operation { if param == nil { return o @@ -223,7 +223,7 @@ func (o *Operation) AddParam(param *Parameter) *Operation { return o } -// RemoveParam removes a parameter from the operation +// RemoveParam removes a parameter from the operation. func (o *Operation) RemoveParam(name, in string) *Operation { for i, p := range o.Parameters { if p.Name == name && p.In == in { @@ -241,14 +241,14 @@ func (o *Operation) SecuredWith(name string, scopes ...string) *Operation { } // WithDefaultResponse adds a default response to the operation. -// Passing a nil value will remove the response +// Passing a nil value will remove the response. func (o *Operation) WithDefaultResponse(response *Response) *Operation { return o.RespondsWith(0, response) } // RespondsWith adds a status code response to the operation. // When the code is 0 the value of the response will be used as default response value. -// When the value of the response is nil it will be removed from the operation +// When the value of the response is nil it will be removed from the operation. func (o *Operation) RespondsWith(code int, response *Response) *Operation { if o.Responses == nil { o.Responses = new(Responses) @@ -279,7 +279,7 @@ type gobAlias struct { SecurityIsEmpty bool } -// GobEncode provides a safe gob encoder for Operation, including empty security requirements +// GobEncode provides a safe gob encoder for Operation, including empty security requirements. func (o Operation) GobEncode() ([]byte, error) { raw := struct { Ext VendorExtensible @@ -293,7 +293,7 @@ func (o Operation) GobEncode() ([]byte, error) { return b.Bytes(), err } -// GobDecode provides a safe gob decoder for Operation, including empty security requirements +// GobDecode provides a safe gob decoder for Operation, including empty security requirements. func (o *Operation) GobDecode(b []byte) error { var raw struct { Ext VendorExtensible @@ -310,7 +310,7 @@ func (o *Operation) GobDecode(b []byte) error { return nil } -// GobEncode provides a safe gob encoder for Operation, including empty security requirements +// GobEncode provides a safe gob encoder for Operation, including empty security requirements. func (op OperationProps) GobEncode() ([]byte, error) { raw := gobAlias{ Alias: (*opsAlias)(&op), @@ -355,7 +355,7 @@ func (op OperationProps) GobEncode() ([]byte, error) { return b.Bytes(), err } -// GobDecode provides a safe gob decoder for Operation, including empty security requirements +// GobDecode provides a safe gob decoder for Operation, including empty security requirements. func (op *OperationProps) GobDecode(b []byte) error { var raw gobAlias diff --git a/operation_test.go b/operation_test.go index bd1d2b54..965b50e4 100644 --- a/operation_test.go +++ b/operation_test.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/testify/v2/require" ) -var operation = Operation{ +var operation = Operation{ //nolint:gochecknoglobals // test fixture VendorExtensible: VendorExtensible{ Extensions: map[string]any{ "x-framework": "go-swagger", @@ -71,15 +71,15 @@ func TestSuccessResponse(t *testing.T) { ope := &Operation{} resp, n, f := ope.SuccessResponse() assert.Nil(t, resp) - assert.Equal(t, 0, n) - assert.False(t, f) + assert.EqualT(t, 0, n) + assert.FalseT(t, f) resp, n, f = operation.SuccessResponse() require.NotNil(t, resp) - assert.Equal(t, "void response", resp.Description) + assert.EqualT(t, "void response", resp.Description) - assert.Equal(t, 0, n) - assert.False(t, f) + assert.EqualT(t, 0, n) + assert.FalseT(t, f) require.NoError(t, json.Unmarshal([]byte(operationJSON), ope)) @@ -90,10 +90,10 @@ func TestSuccessResponse(t *testing.T) { }) resp, n, f = ope.SuccessResponse() require.NotNil(t, resp) - assert.Equal(t, "void response", resp.Description) + assert.EqualT(t, "void response", resp.Description) - assert.Equal(t, 0, n) - assert.False(t, f) + assert.EqualT(t, 0, n) + assert.FalseT(t, f) ope = ope.RespondsWith(200, &Response{ ResponseProps: ResponseProps{ @@ -103,10 +103,10 @@ func TestSuccessResponse(t *testing.T) { resp, n, f = ope.SuccessResponse() require.NotNil(t, resp) - assert.Equal(t, "success", resp.Description) + assert.EqualT(t, "success", resp.Description) - assert.Equal(t, 200, n) - assert.True(t, f) + assert.EqualT(t, 200, n) + assert.TrueT(t, f) } func TestOperationBuilder(t *testing.T) { @@ -134,10 +134,7 @@ func TestOperationBuilder(t *testing.T) { WithSummary("my summary"). WithExternalDocs("some doc", "https://www.example.com") - jazon, err := json.MarshalIndent(ope, "", " ") - require.NoError(t, err) - - assert.JSONEq(t, `{ + assert.JSONMarshalAsT(t, `{ "operationId": "operationID", "description": "test operation", "summary": "my summary", @@ -187,23 +184,20 @@ func TestOperationBuilder(t *testing.T) { "description": "default" } } - }`, string(jazon)) + }`, ope) // check token lookup token, err := ope.JSONLookup("responses") require.NoError(t, err) - jazon, err = json.MarshalIndent(token, "", " ") - require.NoError(t, err) - - assert.JSONEq(t, `{ + assert.JSONMarshalAsT(t, `{ "200": { "description": "success" }, "default": { "description": "default" } - }`, string(jazon)) + }`, token) // check delete methods ope = ope.RespondsWith(200, nil). @@ -212,10 +206,8 @@ func TestOperationBuilder(t *testing.T) { RemoveParam("fakeParam", "query"). Undeprecate(). WithExternalDocs("", "") - jazon, err = json.MarshalIndent(ope, "", " ") - require.NoError(t, err) - assert.JSONEq(t, `{ + assert.JSONMarshalAsT(t, `{ "security": [ { "scheme-name": [ @@ -242,15 +234,11 @@ func TestOperationBuilder(t *testing.T) { "description": "default" } } - }`, string(jazon)) + }`, ope) } func TestIntegrationOperation(t *testing.T) { - var actual Operation - require.NoError(t, json.Unmarshal([]byte(operationJSON), &actual)) - assert.Equal(t, actual, operation) - - assertParsesJSON(t, operationJSON, operation) + assert.JSONUnmarshalAsT(t, operation, operationJSON) } func TestSecurityProperty(t *testing.T) { @@ -258,7 +246,7 @@ func TestSecurityProperty(t *testing.T) { securityNotSet := OperationProps{} jsonResult, err := json.Marshal(securityNotSet) require.NoError(t, err) - assert.NotContains(t, string(jsonResult), "security", "security key should be omitted when unset") + assert.StringNotContainsT(t, string(jsonResult), "security", "security key should be omitted when unset") // Ensure we preserve the security key when it contains an empty (zero length) slice securityContainsEmptyArray := OperationProps{ @@ -344,10 +332,11 @@ func doTestOperationGobEncoding(t *testing.T, fixture string) { } func doTestAnyGobEncoding(t *testing.T, src, dst any) { - expectedJSON, _ := json.MarshalIndent(src, "", " ") + expectedJSON, err := json.MarshalIndent(src, "", " ") + require.NoError(t, err) var b bytes.Buffer - err := gob.NewEncoder(&b).Encode(src) + err = gob.NewEncoder(&b).Encode(src) require.NoError(t, err) err = gob.NewDecoder(&b).Decode(dst) @@ -355,5 +344,5 @@ func doTestAnyGobEncoding(t *testing.T, src, dst any) { jazon, err := json.MarshalIndent(dst, "", " ") require.NoError(t, err) - assert.JSONEq(t, string(expectedJSON), string(jazon)) + assert.JSONEqT(t, string(expectedJSON), string(jazon)) } diff --git a/parameter.go b/parameter.go index b94b7682..516f5d95 100644 --- a/parameter.go +++ b/parameter.go @@ -11,45 +11,51 @@ import ( "github.com/go-openapi/swag/jsonutils" ) -// QueryParam creates a query parameter +// QueryParam creates a query parameter. func QueryParam(name string) *Parameter { return &Parameter{ParamProps: ParamProps{Name: name, In: "query"}} } -// HeaderParam creates a header parameter, this is always required by default +// HeaderParam creates a header parameter, this is always required by default. func HeaderParam(name string) *Parameter { return &Parameter{ParamProps: ParamProps{Name: name, In: "header", Required: true}} } -// PathParam creates a path parameter, this is always required +// PathParam creates a path parameter, this is always required. func PathParam(name string) *Parameter { return &Parameter{ParamProps: ParamProps{Name: name, In: "path", Required: true}} } -// BodyParam creates a body parameter +// BodyParam creates a body parameter. func BodyParam(name string, schema *Schema) *Parameter { return &Parameter{ParamProps: ParamProps{Name: name, In: "body", Schema: schema}} } -// FormDataParam creates a body parameter +// FormDataParam creates a body parameter. func FormDataParam(name string) *Parameter { return &Parameter{ParamProps: ParamProps{Name: name, In: "formData"}} } -// FileParam creates a body 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) +// 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 +// ParamRef creates a parameter that's a json reference. func ParamRef(uri string) *Parameter { p := new(Parameter) p.Ref = MustCreateRef(uri) @@ -60,7 +66,7 @@ func ParamRef(uri string) *Parameter { // // NOTE: // - Schema is defined when "in" == "body": see validate -// - AllowEmptyValue is allowed where "in" == "query" || "formData" +// - AllowEmptyValue is allowed where "in" == "query" || "formData". type ParamProps struct { Description string `json:"description,omitempty"` Name string `json:"name,omitempty"` @@ -104,7 +110,7 @@ type Parameter struct { ParamProps } -// JSONLookup look up a value by the json property name +// JSONLookup look up a value by the json property name. func (p Parameter) JSONLookup(token string) (any, error) { if ex, ok := p.Extensions[token]; ok { return &ex, nil @@ -131,32 +137,32 @@ func (p Parameter) JSONLookup(token string) (any, error) { return r, err } -// WithDescription a fluent builder method for the description of the parameter +// WithDescription a fluent builder method for the description of the parameter. func (p *Parameter) WithDescription(description string) *Parameter { p.Description = description return p } -// Named a fluent builder method to override the name of the parameter +// Named a fluent builder method to override the name of the parameter. func (p *Parameter) Named(name string) *Parameter { p.Name = name return p } -// WithLocation a fluent builder method to override the location of the parameter +// WithLocation a fluent builder method to override the location of the parameter. func (p *Parameter) WithLocation(in string) *Parameter { p.In = in return p } -// Typed a fluent builder method for the type of the parameter value +// Typed a fluent builder method for the type of the parameter value. func (p *Parameter) Typed(tpe, format string) *Parameter { p.Type = tpe p.Format = format return p } -// CollectionOf a fluent builder method for an array parameter +// CollectionOf a fluent builder method for an array parameter. func (p *Parameter) CollectionOf(items *Items, format string) *Parameter { p.Type = jsonArray p.Items = items @@ -164,32 +170,32 @@ func (p *Parameter) CollectionOf(items *Items, format string) *Parameter { return p } -// WithDefault sets the default value on this parameter +// WithDefault sets the default value on this parameter. func (p *Parameter) WithDefault(defaultValue any) *Parameter { p.AsOptional() // with default implies optional p.Default = defaultValue return p } -// AllowsEmptyValues flags this parameter as being ok with empty values +// AllowsEmptyValues flags this parameter as being ok with empty values. func (p *Parameter) AllowsEmptyValues() *Parameter { p.AllowEmptyValue = true return p } -// NoEmptyValues flags this parameter as not liking empty values +// NoEmptyValues flags this parameter as not liking empty values. func (p *Parameter) NoEmptyValues() *Parameter { p.AllowEmptyValue = false return p } -// AsOptional flags this parameter as optional +// AsOptional flags this parameter as optional. func (p *Parameter) AsOptional() *Parameter { p.Required = false return p } -// AsRequired flags this parameter as required +// AsRequired flags this parameter as required. func (p *Parameter) AsRequired() *Parameter { if p.Default != nil { // with a default required makes no sense return p @@ -198,81 +204,81 @@ func (p *Parameter) AsRequired() *Parameter { return p } -// WithMaxLength sets a max length value +// WithMaxLength sets a max length value. func (p *Parameter) WithMaxLength(maximum int64) *Parameter { p.MaxLength = &maximum return p } -// WithMinLength sets a min length value +// WithMinLength sets a min length value. func (p *Parameter) WithMinLength(minimum int64) *Parameter { p.MinLength = &minimum return p } -// WithPattern sets a pattern value +// WithPattern sets a pattern value. func (p *Parameter) WithPattern(pattern string) *Parameter { p.Pattern = pattern return p } -// WithMultipleOf sets a multiple of value +// WithMultipleOf sets a multiple of value. func (p *Parameter) WithMultipleOf(number float64) *Parameter { p.MultipleOf = &number return p } -// WithMaximum sets a maximum number value +// WithMaximum sets a maximum number value. func (p *Parameter) WithMaximum(maximum float64, exclusive bool) *Parameter { p.Maximum = &maximum p.ExclusiveMaximum = exclusive return p } -// WithMinimum sets a minimum number value +// WithMinimum sets a minimum number value. func (p *Parameter) WithMinimum(minimum float64, exclusive bool) *Parameter { p.Minimum = &minimum p.ExclusiveMinimum = exclusive return p } -// WithEnum sets a the enum values (replace) +// WithEnum sets a the enum values (replace). func (p *Parameter) WithEnum(values ...any) *Parameter { p.Enum = append([]any{}, values...) return p } -// WithMaxItems sets the max items +// WithMaxItems sets the max items. func (p *Parameter) WithMaxItems(size int64) *Parameter { p.MaxItems = &size return p } -// WithMinItems sets the min items +// WithMinItems sets the min items. func (p *Parameter) WithMinItems(size int64) *Parameter { p.MinItems = &size return p } -// UniqueValues dictates that this array can only have unique items +// UniqueValues dictates that this array can only have unique items. func (p *Parameter) UniqueValues() *Parameter { p.UniqueItems = true return p } -// AllowDuplicates this array can have duplicates +// AllowDuplicates this array can have duplicates. func (p *Parameter) AllowDuplicates() *Parameter { p.UniqueItems = false return p } -// WithValidations is a fluent method to set parameter validations +// WithValidations is a fluent method to set parameter validations. func (p *Parameter) WithValidations(val CommonValidations) *Parameter { p.SetValidations(SchemaValidations{CommonValidations: val}) return p } -// UnmarshalJSON hydrates this items instance with the data from JSON +// UnmarshalJSON hydrates this items instance with the data from JSON. func (p *Parameter) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &p.CommonValidations); err != nil { return err @@ -289,7 +295,7 @@ func (p *Parameter) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &p.ParamProps) } -// MarshalJSON converts this items object to JSON +// MarshalJSON converts this items object to JSON. func (p Parameter) MarshalJSON() ([]byte, error) { b1, err := json.Marshal(p.CommonValidations) if err != nil { diff --git a/parameters_test.go b/parameters_test.go index 6f2f0f2d..9d8a7623 100644 --- a/parameters_test.go +++ b/parameters_test.go @@ -12,7 +12,7 @@ import ( "github.com/go-openapi/testify/v2/require" ) -var parameter = Parameter{ +var parameter = Parameter{ //nolint:gochecknoglobals // test fixture VendorExtensible: VendorExtensible{Extensions: map[string]any{ "x-framework": "swagger-go", }}, @@ -49,6 +49,7 @@ var parameter = Parameter{ }, } +//nolint:gochecknoglobals // test fixture var parameterJSON = `{ "items": { "$ref": "Cat" @@ -81,11 +82,7 @@ var parameterJSON = `{ }` func TestIntegrationParameter(t *testing.T) { - var actual Parameter - require.NoError(t, json.Unmarshal([]byte(parameterJSON), &actual)) - assert.Equal(t, actual, parameter) - - assertParsesJSON(t, parameterJSON, parameter) + assert.JSONUnmarshalAsT(t, parameter, parameterJSON) } func TestParameterSerialization(t *testing.T) { @@ -97,27 +94,28 @@ func TestParameterSerialization(t *testing.T) { SimpleSchema: SimpleSchema{Type: "int", Format: "int32"}, } - assertSerializeJSON(t, QueryParam("").Typed("string", ""), `{"type":"string","in":"query"}`) + assert.JSONMarshalAsT(t, `{"type":"string","in":"query"}`, QueryParam("").Typed("string", "")) + + assert.JSONMarshalAsT(t, + `{"type":"array","items":{"type":"string"},"collectionFormat":"multi","in":"query"}`, + QueryParam("").CollectionOf(items, "multi")) - assertSerializeJSON(t, - QueryParam("").CollectionOf(items, "multi"), - `{"type":"array","items":{"type":"string"},"collectionFormat":"multi","in":"query"}`) + assert.JSONMarshalAsT(t, `{"type":"string","in":"path","required":true}`, PathParam("").Typed("string", "")) - assertSerializeJSON(t, PathParam("").Typed("string", ""), `{"type":"string","in":"path","required":true}`) + assert.JSONMarshalAsT(t, + `{"type":"array","items":{"type":"string"},"collectionFormat":"multi","in":"path","required":true}`, + PathParam("").CollectionOf(items, "multi")) - assertSerializeJSON(t, - PathParam("").CollectionOf(items, "multi"), - `{"type":"array","items":{"type":"string"},"collectionFormat":"multi","in":"path","required":true}`) + assert.JSONMarshalAsT(t, + `{"type":"array","items":{"type":"int","format":"int32"},"collectionFormat":"multi","in":"path","required":true}`, + PathParam("").CollectionOf(intItems, "multi")) - assertSerializeJSON(t, - PathParam("").CollectionOf(intItems, "multi"), - `{"type":"array","items":{"type":"int","format":"int32"},"collectionFormat":"multi","in":"path","required":true}`) + assert.JSONMarshalAsT(t, `{"type":"string","in":"header","required":true}`, HeaderParam("").Typed("string", "")) - assertSerializeJSON(t, HeaderParam("").Typed("string", ""), `{"type":"string","in":"header","required":true}`) + assert.JSONMarshalAsT(t, + `{"type":"array","items":{"type":"string"},"collectionFormat":"multi","in":"header","required":true}`, + HeaderParam("").CollectionOf(items, "multi")) - assertSerializeJSON(t, - HeaderParam("").CollectionOf(items, "multi"), - `{"type":"array","items":{"type":"string"},"collectionFormat":"multi","in":"header","required":true}`) schema := &Schema{SchemaProps: SchemaProps{ Properties: map[string]Schema{ "name": {SchemaProps: SchemaProps{ @@ -130,19 +128,18 @@ func TestParameterSerialization(t *testing.T) { SchemaProps: SchemaProps{Ref: MustCreateRef("Cat")}, } - assertSerializeJSON(t, - BodyParam("", schema), - `{"in":"body","schema":{"properties":{"name":{"type":"string"}}}}`) + assert.JSONMarshalAsT(t, + `{"in":"body","schema":{"properties":{"name":{"type":"string"}}}}`, + BodyParam("", schema)) - assertSerializeJSON(t, - BodyParam("", refSchema), - `{"in":"body","schema":{"$ref":"Cat"}}`) + assert.JSONMarshalAsT(t, + `{"in":"body","schema":{"$ref":"Cat"}}`, + BodyParam("", refSchema)) // array body param - assertSerializeJSON(t, - BodyParam("", ArrayProperty(RefProperty("Cat"))), - `{"in":"body","schema":{"type":"array","items":{"$ref":"Cat"}}}`) - + assert.JSONMarshalAsT(t, + `{"in":"body","schema":{"type":"array","items":{"$ref":"Cat"}}}`, + BodyParam("", ArrayProperty(RefProperty("Cat")))) } func TestParameterGobEncoding(t *testing.T) { diff --git a/path_item.go b/path_item.go index c692b89e..4408ece4 100644 --- a/path_item.go +++ b/path_item.go @@ -10,7 +10,7 @@ import ( "github.com/go-openapi/swag/jsonutils" ) -// PathItemProps the path item specific properties +// PathItemProps the path item specific properties. type PathItemProps struct { Get *Operation `json:"get,omitempty"` Put *Operation `json:"put,omitempty"` @@ -34,7 +34,7 @@ type PathItem struct { PathItemProps } -// JSONLookup look up a value by the json property name +// JSONLookup look up a value by the json property name. func (p PathItem) JSONLookup(token string) (any, error) { if ex, ok := p.Extensions[token]; ok { return &ex, nil @@ -46,7 +46,7 @@ func (p PathItem) JSONLookup(token string) (any, error) { return r, err } -// UnmarshalJSON hydrates this items instance with the data from JSON +// UnmarshalJSON hydrates this items instance with the data from JSON. func (p *PathItem) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &p.Refable); err != nil { return err @@ -57,7 +57,7 @@ func (p *PathItem) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &p.PathItemProps) } -// MarshalJSON converts this items object to JSON +// MarshalJSON converts this items object to JSON. func (p PathItem) MarshalJSON() ([]byte, error) { b3, err := json.Marshal(p.Refable) if err != nil { diff --git a/path_item_test.go b/path_item_test.go index ddb5e10d..8a08f6a8 100644 --- a/path_item_test.go +++ b/path_item_test.go @@ -4,14 +4,12 @@ package spec import ( - "encoding/json" "testing" "github.com/go-openapi/testify/v2/assert" - "github.com/go-openapi/testify/v2/require" ) -var pathItem = PathItem{ +var pathItem = PathItem{ //nolint:gochecknoglobals // test fixture Refable: Refable{Ref: MustCreateRef("Dog")}, VendorExtensible: VendorExtensible{ Extensions: map[string]any{ @@ -62,9 +60,5 @@ const pathItemJSON = `{ }` func TestIntegrationPathItem(t *testing.T) { - var actual PathItem - require.NoError(t, json.Unmarshal([]byte(pathItemJSON), &actual)) - assert.Equal(t, actual, pathItem) - - assertParsesJSON(t, pathItemJSON, pathItem) + assert.JSONUnmarshalAsT(t, pathItem, pathItemJSON) } diff --git a/paths.go b/paths.go index b9e42184..5daf5a67 100644 --- a/paths.go +++ b/paths.go @@ -23,7 +23,7 @@ type Paths struct { 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 +// JSONLookup look up a value by the json property name. func (p Paths) JSONLookup(token string) (any, error) { if pi, ok := p.Paths[token]; ok { return &pi, nil @@ -34,7 +34,7 @@ func (p Paths) JSONLookup(token string) (any, error) { return nil, fmt.Errorf("object has no field %q: %w", token, ErrSpec) } -// UnmarshalJSON hydrates this items instance with the data from JSON +// UnmarshalJSON hydrates this items instance with the data from JSON. func (p *Paths) UnmarshalJSON(data []byte) error { var res map[string]json.RawMessage if err := json.Unmarshal(data, &res); err != nil { @@ -65,7 +65,7 @@ func (p *Paths) UnmarshalJSON(data []byte) error { return nil } -// MarshalJSON converts this items object to JSON +// MarshalJSON converts this items object to JSON. func (p Paths) MarshalJSON() ([]byte, error) { b1, err := json.Marshal(p.VendorExtensible) if err != nil { diff --git a/paths_test.go b/paths_test.go index 79dce173..3057a9da 100644 --- a/paths_test.go +++ b/paths_test.go @@ -4,14 +4,12 @@ package spec import ( - "encoding/json" "testing" "github.com/go-openapi/testify/v2/assert" - "github.com/go-openapi/testify/v2/require" ) -var paths = Paths{ +var paths = Paths{ //nolint:gochecknoglobals // test fixture VendorExtensible: VendorExtensible{Extensions: map[string]any{"x-framework": "go-swagger"}}, Paths: map[string]PathItem{ "/": { @@ -23,10 +21,5 @@ var paths = Paths{ 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.Equal(t, actual, paths) - - assertParsesJSON(t, pathsJSON, paths) - + assert.JSONUnmarshalAsT(t, paths, pathsJSON) } diff --git a/properties.go b/properties.go index c4988180..b8e97271 100644 --- a/properties.go +++ b/properties.go @@ -10,7 +10,7 @@ import ( "sort" ) -// OrderSchemaItem holds a named schema (e.g. from a property of an object) +// OrderSchemaItem holds a named schema (e.g. from a property of an object). type OrderSchemaItem struct { Schema @@ -53,7 +53,7 @@ func (items OrderSchemaItems) Swap(i, j int) { items[i], items[j] = items[j], it func (items OrderSchemaItems) Less(i, j int) (ret bool) { ii, oki := items[i].Extensions.GetInt("x-order") ij, okj := items[j].Extensions.GetInt("x-order") - if oki { + if oki { //nolint:nestif // nested recover logic for safe type comparison if okj { defer func() { if err := recover(); err != nil { @@ -94,7 +94,7 @@ func (items OrderSchemaItems) marshalJSONItem(item OrderSchemaItem, output *byte // It knows how to transform its keys into an ordered slice. type SchemaProperties map[string]Schema -// ToOrderedSchemaItems transforms the map of properties into a sortable slice +// ToOrderedSchemaItems transforms the map of properties into a sortable slice. func (properties SchemaProperties) ToOrderedSchemaItems() OrderSchemaItems { items := make(OrderSchemaItems, 0, len(properties)) for k, v := range properties { diff --git a/properties_test.go b/properties_test.go index 8475dfe2..1c59acab 100644 --- a/properties_test.go +++ b/properties_test.go @@ -6,6 +6,7 @@ package spec import ( "testing" + "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/testify/v2/require" ) @@ -20,7 +21,7 @@ func TestPropertySerialization(t *testing.T) { }}, }} - var propSerData = []struct { + propSerData := []struct { Schema *Schema JSON string }{ @@ -42,10 +43,9 @@ func TestPropertySerialization(t *testing.T) { for _, v := range propSerData { t.Log("roundtripping for", v.JSON) - assertSerializeJSON(t, v.Schema, v.JSON) - assertParsesJSON(t, v.JSON, v.Schema) + assert.JSONMarshalAsT(t, v.JSON, v.Schema) + assert.JSONUnmarshalAsT(t, v.Schema, v.JSON) } - } func TestOrderedSchemaItem_Issue216(t *testing.T) { diff --git a/ref.go b/ref.go index 1d1c7591..40b7d486 100644 --- a/ref.go +++ b/ref.go @@ -14,28 +14,28 @@ import ( "github.com/go-openapi/jsonreference" ) -// Refable is a struct for things that accept a $ref property +// Refable is a struct for things that accept a $ref property. type Refable struct { Ref Ref } -// MarshalJSON marshals the ref to json +// MarshalJSON marshals the ref to json. func (r Refable) MarshalJSON() ([]byte, error) { return r.Ref.MarshalJSON() } -// UnmarshalJSON unmarshals 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 +// 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 { @@ -51,7 +51,7 @@ func MustCreateRef(refURI string) Ref { return Ref{Ref: jsonreference.MustCreateRef(refURI)} } -// RemoteURI gets the remote uri part of the ref +// RemoteURI gets the remote uri part of the ref. func (r *Ref) RemoteURI() string { if r.String() == "" { return "" @@ -62,7 +62,7 @@ func (r *Ref) RemoteURI() string { return u.String() } -// IsValidURI returns true when the url the ref points to can be found +// IsValidURI returns true when the url the ref points to can be found. func (r *Ref) IsValidURI(basepaths ...string) bool { if r.String() == "" { return true @@ -112,7 +112,7 @@ func (r *Ref) IsValidURI(basepaths ...string) bool { } // Inherits creates a new reference from a parent and a child -// If the child cannot inherit from the parent, an error is returned +// If the child cannot inherit from the parent, an error is returned. func (r *Ref) Inherits(child Ref) (*Ref, error) { ref, err := r.Ref.Inherits(child.Ref) if err != nil { @@ -121,7 +121,7 @@ func (r *Ref) Inherits(child Ref) (*Ref, error) { return &Ref{Ref: *ref}, nil } -// MarshalJSON marshals this ref into a JSON object +// MarshalJSON marshals this ref into a JSON object. func (r Ref) MarshalJSON() ([]byte, error) { str := r.String() if str == "" { @@ -134,7 +134,7 @@ func (r Ref) MarshalJSON() ([]byte, error) { return json.Marshal(v) } -// UnmarshalJSON unmarshals this ref from a JSON object +// UnmarshalJSON unmarshals this ref from a JSON object. func (r *Ref) UnmarshalJSON(d []byte) error { var v map[string]any if err := json.Unmarshal(d, &v); err != nil { @@ -143,7 +143,7 @@ func (r *Ref) UnmarshalJSON(d []byte) error { return r.fromMap(v) } -// GobEncode provides a safe gob encoder for Ref +// GobEncode provides a safe gob encoder for Ref. func (r Ref) GobEncode() ([]byte, error) { var b bytes.Buffer raw, err := r.MarshalJSON() @@ -154,7 +154,7 @@ func (r Ref) GobEncode() ([]byte, error) { return b.Bytes(), err } -// GobDecode provides a safe gob decoder for Ref +// GobDecode provides a safe gob decoder for Ref. func (r *Ref) GobDecode(b []byte) error { var raw []byte buf := bytes.NewBuffer(b) diff --git a/ref_test.go b/ref_test.go index d9e238c9..8973af20 100644 --- a/ref_test.go +++ b/ref_test.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/testify/v2/require" ) -// pin pointing go-swagger/go-swagger#1816 issue with cloning ref's +// pin pointing go-swagger/go-swagger#1816 issue with cloning ref's. func TestCloneRef(t *testing.T) { var b bytes.Buffer src := MustCreateRef("#/definitions/test") @@ -29,5 +29,5 @@ func TestCloneRef(t *testing.T) { jazon, err := json.Marshal(dst) require.NoError(t, err) - assert.JSONEq(t, `{"$ref":"#/definitions/test"}`, string(jazon)) + assert.JSONEqT(t, `{"$ref":"#/definitions/test"}`, string(jazon)) } diff --git a/resolver.go b/resolver.go index 600574e1..1bf90c86 100644 --- a/resolver.go +++ b/resolver.go @@ -20,7 +20,7 @@ func resolveAnyWithBase(root any, ref *Ref, result any, options *ExpandOptions) return nil } -// ResolveRefWithBase resolves a reference against a context root with preservation of base path +// ResolveRefWithBase resolves a reference against a context root with preservation of base path. func ResolveRefWithBase(root any, ref *Ref, options *ExpandOptions) (*Schema, error) { result := new(Schema) @@ -34,7 +34,7 @@ func ResolveRefWithBase(root any, ref *Ref, options *ExpandOptions) (*Schema, er // ResolveRef resolves a reference for a schema against a context root // ref is guaranteed to be in root (no need to go to external files) // -// ResolveRef is ONLY called from the code generation module +// ResolveRef is ONLY called from the code generation module. func ResolveRef(root any, ref *Ref) (*Schema, error) { res, _, err := ref.GetPointer().Get(root) if err != nil { @@ -57,7 +57,7 @@ func ResolveRef(root any, ref *Ref) (*Schema, error) { } } -// ResolveParameterWithBase resolves a parameter reference against a context root and base path +// ResolveParameterWithBase resolves a parameter reference against a context root and base path. func ResolveParameterWithBase(root any, ref Ref, options *ExpandOptions) (*Parameter, error) { result := new(Parameter) @@ -68,12 +68,12 @@ func ResolveParameterWithBase(root any, ref Ref, options *ExpandOptions) (*Param return result, nil } -// ResolveParameter resolves a parameter reference against a context root +// ResolveParameter resolves a parameter reference against a context root. 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 +// ResolveResponseWithBase resolves response a reference against a context root and base path. func ResolveResponseWithBase(root any, ref Ref, options *ExpandOptions) (*Response, error) { result := new(Response) @@ -85,12 +85,12 @@ func ResolveResponseWithBase(root any, ref Ref, options *ExpandOptions) (*Respon return result, nil } -// ResolveResponse resolves response a reference against a context root +// ResolveResponse resolves response a reference against a context root. 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 +// ResolvePathItemWithBase resolves response a path item against a context root and base path. func ResolvePathItemWithBase(root any, ref Ref, options *ExpandOptions) (*PathItem, error) { result := new(PathItem) @@ -103,7 +103,7 @@ func ResolvePathItemWithBase(root any, ref Ref, options *ExpandOptions) (*PathIt // ResolvePathItem resolves response a path item against a context root and base path // -// Deprecated: use ResolvePathItemWithBase instead +// Deprecated: use ResolvePathItemWithBase instead. func ResolvePathItem(root any, ref Ref, options *ExpandOptions) (*PathItem, error) { return ResolvePathItemWithBase(root, ref, options) } @@ -124,7 +124,7 @@ func ResolveItemsWithBase(root any, ref Ref, options *ExpandOptions) (*Items, er // ResolveItems resolves parameter items reference against a context root and base path. // -// Deprecated: use ResolveItemsWithBase instead +// Deprecated: use ResolveItemsWithBase instead. 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 e8d6311e..c949eca0 100644 --- a/resolver_test.go +++ b/resolver_test.go @@ -5,8 +5,6 @@ package spec import ( "encoding/json" - "net/http" - "net/http/httptest" "os" "path/filepath" "testing" @@ -18,7 +16,7 @@ import ( func TestResolveRef(t *testing.T) { var root any - require.NoError(t, json.Unmarshal([]byte(PetStore20), &root)) + require.NoError(t, json.Unmarshal(PetStore20, &root)) ref, err := NewRef("#/definitions/Category") require.NoError(t, err) @@ -29,7 +27,7 @@ func TestResolveRef(t *testing.T) { b, err := sch.MarshalJSON() require.NoError(t, err) - assert.JSONEq(t, `{"id":"Category","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}}}`, string(b)) + assert.JSONEqT(t, `{"id":"Category","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}}}`, string(b)) // WithBase variant sch, err = ResolveRefWithBase(root, &ref, &ExpandOptions{ @@ -40,7 +38,7 @@ func TestResolveRef(t *testing.T) { b, err = sch.MarshalJSON() require.NoError(t, err) - assert.JSONEq(t, `{"id":"Category","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}}}`, string(b)) + assert.JSONEqT(t, `{"id":"Category","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}}}`, string(b)) } func TestResolveResponse(t *testing.T) { @@ -58,7 +56,7 @@ func TestResolveResponse(t *testing.T) { // resolve resolves the ref, but dos not expand jazon := asJSON(t, resp2) - assert.JSONEq(t, `{ + assert.JSONEqT(t, `{ "$ref": "#/responses/petResponse" }`, jazon) } @@ -78,7 +76,7 @@ func TestResolveResponseWithBase(t *testing.T) { // resolve resolves the ref, but dos not expand jazon := asJSON(t, resp2) - assert.JSONEq(t, `{ + assert.JSONEqT(t, `{ "$ref": "#/responses/petResponse" }`, jazon) } @@ -96,7 +94,7 @@ func TestResolveParam(t *testing.T) { jazon := asJSON(t, par) - assert.JSONEq(t, `{ + assert.JSONEqT(t, `{ "name": "id", "in": "path", "description": "ID of pet to fetch", @@ -119,7 +117,7 @@ func TestResolveParamWithBase(t *testing.T) { jazon := asJSON(t, par) - assert.JSONEq(t, `{ + assert.JSONEqT(t, `{ "description":"ID of pet to fetch", "format":"int64", "in":"path", @@ -130,9 +128,7 @@ func TestResolveParamWithBase(t *testing.T) { } func TestResolveRemoteRef_RootSame(t *testing.T) { - fileserver := http.FileServer(http.Dir(specs)) - server := httptest.NewServer(fileserver) - defer server.Close() + server := fixtureServer(t, specs) rootDoc := new(Swagger) b, err := os.ReadFile(filepath.Join(specs, "refed.json")) @@ -158,9 +154,7 @@ func TestResolveRemoteRef_RootSame(t *testing.T) { } func TestResolveRemoteRef_FromFragment(t *testing.T) { - fileserver := http.FileServer(http.Dir(specs)) - server := httptest.NewServer(fileserver) - defer server.Close() + server := fixtureServer(t, specs) rootDoc := new(Swagger) b, err := os.ReadFile(filepath.Join(specs, "refed.json")) @@ -178,9 +172,7 @@ func TestResolveRemoteRef_FromFragment(t *testing.T) { } func TestResolveRemoteRef_FromInvalidFragment(t *testing.T) { - fileserver := http.FileServer(http.Dir(specs)) - server := httptest.NewServer(fileserver) - defer server.Close() + server := fixtureServer(t, specs) rootDoc := new(Swagger) b, err := os.ReadFile(filepath.Join(specs, "refed.json")) @@ -215,9 +207,7 @@ func TestResolveRemoteRef_FromInvalidFragment(t *testing.T) { // } func TestResolveRemoteRef_ToParameter(t *testing.T) { - fileserver := http.FileServer(http.Dir(specs)) - server := httptest.NewServer(fileserver) - defer server.Close() + server := fixtureServer(t, specs) rootDoc := new(Swagger) b, err := os.ReadFile(filepath.Join(specs, "refed.json")) @@ -231,18 +221,16 @@ func TestResolveRemoteRef_ToParameter(t *testing.T) { resolver := defaultSchemaLoader(rootDoc, nil, nil, nil) require.NoError(t, resolver.Resolve(&ref, &tgt, "")) - assert.Equal(t, "id", tgt.Name) - assert.Equal(t, "path", tgt.In) - assert.Equal(t, "ID of pet to fetch", tgt.Description) - assert.True(t, tgt.Required) - assert.Equal(t, "integer", tgt.Type) - assert.Equal(t, "int64", tgt.Format) + assert.EqualT(t, "id", tgt.Name) + assert.EqualT(t, "path", tgt.In) + assert.EqualT(t, "ID of pet to fetch", tgt.Description) + assert.TrueT(t, tgt.Required) + assert.EqualT(t, "integer", tgt.Type) + assert.EqualT(t, "int64", tgt.Format) } func TestResolveRemoteRef_ToPathItem(t *testing.T) { - fileserver := http.FileServer(http.Dir(specs)) - server := httptest.NewServer(fileserver) - defer server.Close() + server := fixtureServer(t, specs) rootDoc := new(Swagger) b, err := os.ReadFile(filepath.Join(specs, "refed.json")) @@ -259,9 +247,7 @@ func TestResolveRemoteRef_ToPathItem(t *testing.T) { } func TestResolveRemoteRef_ToResponse(t *testing.T) { - fileserver := http.FileServer(http.Dir(specs)) - server := httptest.NewServer(fileserver) - defer server.Close() + server := fixtureServer(t, specs) rootDoc := new(Swagger) b, err := os.ReadFile(filepath.Join(specs, "refed.json")) @@ -298,7 +284,7 @@ func TestResolveLocalRef_FromFragment(t *testing.T) { resolver := defaultSchemaLoader(rootDoc, nil, nil, nil) require.NoError(t, resolver.Resolve(&ref, &tgt, "")) - assert.Equal(t, "Category", tgt.ID) + assert.EqualT(t, "Category", tgt.ID) } func TestResolveLocalRef_FromInvalidFragment(t *testing.T) { @@ -328,12 +314,12 @@ func TestResolveLocalRef_Parameter(t *testing.T) { resolver := defaultSchemaLoader(rootDoc, nil, nil, nil) require.NoError(t, resolver.Resolve(&ref, &tgt, basePath)) - assert.Equal(t, "id", tgt.Name) - assert.Equal(t, "path", tgt.In) - assert.Equal(t, "ID of pet to fetch", tgt.Description) - assert.True(t, tgt.Required) - assert.Equal(t, "integer", tgt.Type) - assert.Equal(t, "int64", tgt.Format) + assert.EqualT(t, "id", tgt.Name) + assert.EqualT(t, "path", tgt.In) + assert.EqualT(t, "ID of pet to fetch", tgt.Description) + assert.TrueT(t, tgt.Required) + assert.EqualT(t, "integer", tgt.Type) + assert.EqualT(t, "int64", tgt.Format) } func TestResolveLocalRef_PathItem(t *testing.T) { @@ -384,7 +370,7 @@ func TestResolvePathItem(t *testing.T) { jazon := asJSON(t, pathItem) - assert.JSONEq(t, `{ + assert.JSONEqT(t, `{ "get": { "responses": { "200": { @@ -419,7 +405,7 @@ func TestResolveExtraItem(t *testing.T) { jazon := asJSON(t, parmItem) - assert.JSONEq(t, `{ + assert.JSONEqT(t, `{ "type": "integer", "format": "int32" }`, jazon) @@ -431,7 +417,7 @@ func TestResolveExtraItem(t *testing.T) { jazon = asJSON(t, hdrItem) - assert.JSONEq(t, `{ + assert.JSONEqT(t, `{ "type": "string", "format": "uuid" }`, jazon) diff --git a/response.go b/response.go index e5a7e5c4..4bb6a2bc 100644 --- a/response.go +++ b/response.go @@ -10,7 +10,7 @@ import ( "github.com/go-openapi/swag/jsonutils" ) -// ResponseProps properties specific to a response +// ResponseProps properties specific to a response. type ResponseProps struct { Description string `json:"description"` Schema *Schema `json:"schema,omitempty"` @@ -27,19 +27,19 @@ type Response struct { VendorExtensible } -// NewResponse creates a new response instance +// NewResponse creates a new response instance. func NewResponse() *Response { return new(Response) } -// ResponseRef creates a response as a json reference +// 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 +// JSONLookup look up a value by the json property name. func (r Response) JSONLookup(token string) (any, error) { if ex, ok := r.Extensions[token]; ok { return &ex, nil @@ -51,7 +51,7 @@ func (r Response) JSONLookup(token string) (any, error) { return ptr, err } -// UnmarshalJSON hydrates this items instance with the data from JSON +// UnmarshalJSON hydrates this items instance with the data from JSON. func (r *Response) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &r.ResponseProps); err != nil { return err @@ -62,7 +62,7 @@ func (r *Response) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &r.VendorExtensible) } -// MarshalJSON converts this items object to JSON +// MarshalJSON converts this items object to JSON. func (r Response) MarshalJSON() ([]byte, error) { var ( b1 []byte @@ -100,20 +100,20 @@ func (r Response) MarshalJSON() ([]byte, error) { return jsonutils.ConcatJSON(b1, b2, b3), nil } -// WithDescription sets the description on this response, allows for chaining +// WithDescription sets the description on this response, allows for chaining. func (r *Response) WithDescription(description string) *Response { r.Description = description return r } // WithSchema sets the schema on this response, allows for chaining. -// Passing a nil argument removes the schema from this response +// Passing a nil argument removes the schema from this response. func (r *Response) WithSchema(schema *Schema) *Response { r.Schema = schema return r } -// AddHeader adds a header to this response +// AddHeader adds a header to this response. func (r *Response) AddHeader(name string, header *Header) *Response { if header == nil { return r.RemoveHeader(name) @@ -125,13 +125,13 @@ func (r *Response) AddHeader(name string, header *Header) *Response { return r } -// RemoveHeader removes a header from this response +// RemoveHeader removes a header from this response. func (r *Response) RemoveHeader(name string) *Response { delete(r.Headers, name) return r } -// AddExample adds an example to this response +// AddExample adds an example to this response. func (r *Response) AddExample(mediaType string, example any) *Response { if r.Examples == nil { r.Examples = make(map[string]any) diff --git a/response_test.go b/response_test.go index cf62d576..dd9045e7 100644 --- a/response_test.go +++ b/response_test.go @@ -18,14 +18,13 @@ package spec import ( - "encoding/json" "testing" "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/testify/v2/require" ) -var response = Response{ +var response = Response{ //nolint:gochecknoglobals // test fixture Refable: Refable{Ref: MustCreateRef("Dog")}, VendorExtensible: VendorExtensible{ Extensions: map[string]any{ @@ -48,11 +47,7 @@ const responseJSON = `{ }` func TestIntegrationResponse(t *testing.T) { - var actual Response - require.NoError(t, json.Unmarshal([]byte(responseJSON), &actual)) - assert.Equal(t, actual, response) - - assertParsesJSON(t, responseJSON, response) + assert.JSONUnmarshalAsT(t, response, responseJSON) } func TestJSONLookupResponse(t *testing.T) { @@ -63,7 +58,7 @@ func TestJSONLookupResponse(t *testing.T) { var ok bool ref, ok := res.(*Ref) - require.True(t, ok) + require.TrueT(t, ok) assert.Equal(t, MustCreateRef("Dog"), *ref) var def string @@ -73,8 +68,8 @@ func TestJSONLookupResponse(t *testing.T) { require.IsType(t, def, res) def, ok = res.(string) - require.True(t, ok) - assert.Equal(t, "Dog exists", def) + require.TrueT(t, ok) + assert.EqualT(t, "Dog exists", def) var x *any res, err = response.JSONLookup("x-go-name") @@ -83,7 +78,7 @@ func TestJSONLookupResponse(t *testing.T) { require.IsType(t, x, res) x, ok = res.(*any) - require.True(t, ok) + require.TrueT(t, ok) assert.EqualValues(t, "PutDogExists", *x) res, err = response.JSONLookup("unknown") @@ -97,10 +92,7 @@ func TestResponseBuild(t *testing.T) { WithSchema(new(Schema).Typed("object", "")). AddHeader("x-header", ResponseHeader().Typed("string", "")). AddExample("application/json", `{"key":"value"}`) - jazon, err := json.MarshalIndent(resp, "", " ") - require.NoError(t, err) - - assert.JSONEq(t, `{ + assert.JSONMarshalAsT(t, `{ "description": "some response", "schema": { "type": "object" @@ -113,5 +105,5 @@ func TestResponseBuild(t *testing.T) { "examples": { "application/json": "{\"key\":\"value\"}" } - }`, string(jazon)) + }`, resp) } diff --git a/responses.go b/responses.go index 733a1315..fb369e4a 100644 --- a/responses.go +++ b/responses.go @@ -31,7 +31,7 @@ type Responses struct { ResponsesProps } -// JSONLookup implements an interface to customize json pointer lookup +// JSONLookup implements an interface to customize json pointer lookup. func (r Responses) JSONLookup(token string) (any, error) { if token == "default" { return r.Default, nil @@ -47,7 +47,7 @@ func (r Responses) JSONLookup(token string) (any, error) { return nil, fmt.Errorf("object has no field %q: %w", token, ErrSpec) } -// UnmarshalJSON hydrates this items instance with the data from JSON +// UnmarshalJSON hydrates this items instance with the data from JSON. func (r *Responses) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &r.ResponsesProps); err != nil { return err @@ -62,7 +62,7 @@ func (r *Responses) UnmarshalJSON(data []byte) error { return nil } -// MarshalJSON converts this items object to JSON +// MarshalJSON converts this items object to JSON. func (r Responses) MarshalJSON() ([]byte, error) { b1, err := json.Marshal(r.ResponsesProps) if err != nil { @@ -84,7 +84,7 @@ type ResponsesProps struct { StatusCodeResponses map[int]Response } -// MarshalJSON marshals responses as JSON +// MarshalJSON marshals responses as JSON. func (r ResponsesProps) MarshalJSON() ([]byte, error) { toser := map[string]Response{} if r.Default != nil { @@ -96,7 +96,7 @@ func (r ResponsesProps) MarshalJSON() ([]byte, error) { return json.Marshal(toser) } -// UnmarshalJSON unmarshals responses from JSON +// UnmarshalJSON unmarshals responses from JSON. func (r *ResponsesProps) UnmarshalJSON(data []byte) error { var res map[string]json.RawMessage if err := json.Unmarshal(data, &res); err != nil { diff --git a/responses_test.go b/responses_test.go index f920ad21..c9810c1d 100644 --- a/responses_test.go +++ b/responses_test.go @@ -18,14 +18,13 @@ package spec import ( - "encoding/json" "testing" "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/testify/v2/require" ) -var responses = Responses{ +var responses = Responses{ //nolint:gochecknoglobals // test fixture VendorExtensible: VendorExtensible{ Extensions: map[string]any{ "x-go-name": "PutDogExists", @@ -62,16 +61,12 @@ const responsesJSON = `{ }` func TestIntegrationResponses(t *testing.T) { - var actual Responses - require.NoError(t, json.Unmarshal([]byte(responsesJSON), &actual)) - assert.Equal(t, actual, responses) - - assertParsesJSON(t, responsesJSON, responses) + assert.JSONUnmarshalAsT(t, responses, responsesJSON) } func TestJSONLookupResponses(t *testing.T) { resp200, ok := responses.StatusCodeResponses[200] - require.True(t, ok) + require.TrueT(t, ok) res, err := resp200.JSONLookup("$ref") require.NoError(t, err) @@ -79,7 +74,7 @@ func TestJSONLookupResponses(t *testing.T) { require.IsType(t, &Ref{}, res) ref, ok := res.(*Ref) - require.True(t, ok) + require.TrueT(t, ok) assert.Equal(t, MustCreateRef("Dog"), *ref) var def string @@ -89,8 +84,8 @@ func TestJSONLookupResponses(t *testing.T) { require.IsType(t, def, res) def, ok = res.(string) - require.True(t, ok) - assert.Equal(t, "Dog exists", def) + require.TrueT(t, ok) + assert.EqualT(t, "Dog exists", def) var x *any res, err = responses.JSONLookup("x-go-name") @@ -99,7 +94,7 @@ func TestJSONLookupResponses(t *testing.T) { require.IsType(t, x, res) x, ok = res.(*any) - require.True(t, ok) + require.TrueT(t, ok) assert.EqualValues(t, "PutDogExists", *x) res, err = responses.JSONLookup("unknown") @@ -113,8 +108,7 @@ func TestResponsesBuild(t *testing.T) { WithSchema(new(Schema).Typed("object", "")). AddHeader("x-header", ResponseHeader().Typed("string", "")). AddExample("application/json", `{"key":"value"}`) - jazon, _ := json.MarshalIndent(resp, "", " ") - assert.JSONEq(t, `{ + assert.JSONMarshalAsT(t, `{ "description": "some response", "schema": { "type": "object" @@ -127,5 +121,5 @@ func TestResponsesBuild(t *testing.T) { "examples": { "application/json": "{\"key\":\"value\"}" } - }`, string(jazon)) + }`, resp) } diff --git a/schema.go b/schema.go index 6623728a..d7a481bf 100644 --- a/schema.go +++ b/schema.go @@ -13,86 +13,88 @@ import ( "github.com/go-openapi/swag/jsonutils" ) -// BooleanProperty creates a boolean property +// BooleanProperty creates a boolean property. func BooleanProperty() *Schema { return &Schema{SchemaProps: SchemaProps{Type: []string{"boolean"}}} } -// BoolProperty creates a boolean property +// BoolProperty creates a boolean property. func BoolProperty() *Schema { return BooleanProperty() } -// StringProperty creates a string property +// StringProperty creates a string property. func StringProperty() *Schema { return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}}} } -// CharProperty creates a string property +// CharProperty creates a string property. func CharProperty() *Schema { return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}}} } -// Float64Property creates a float64/double property +// Float64Property creates a float64/double property. func Float64Property() *Schema { return &Schema{SchemaProps: SchemaProps{Type: []string{"number"}, Format: "double"}} } -// Float32Property creates a float32/float property +// Float32Property creates a float32/float property. func Float32Property() *Schema { return &Schema{SchemaProps: SchemaProps{Type: []string{"number"}, Format: "float"}} } -// Int8Property creates an int8 property +// Int8Property creates an int8 property. func Int8Property() *Schema { return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int8"}} } -// Int16Property creates an int16 property +// Int16Property creates an int16 property. func Int16Property() *Schema { return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int16"}} } -// Int32Property creates an int32 property +// Int32Property creates an int32 property. func Int32Property() *Schema { return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int32"}} } -// Int64Property creates an int64 property +// Int64Property creates an int64 property. func Int64Property() *Schema { return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int64"}} } -// StrFmtProperty creates a property for the named string format +// StrFmtProperty creates a property for the named string format. func StrFmtProperty(format string) *Schema { return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}, Format: format}} } -// DateProperty creates a date property +// DateProperty creates a date property. func DateProperty() *Schema { return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}, Format: "date"}} } -// DateTimeProperty creates a date time property +// DateTimeProperty creates a date time property. func DateTimeProperty() *Schema { return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}, Format: "date-time"}} } -// MapProperty creates a map property +// 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 +// RefProperty creates a ref property. func RefProperty(name string) *Schema { return &Schema{SchemaProps: SchemaProps{Ref: MustCreateRef(name)}} } -// RefSchema creates a ref property +// RefSchema creates a ref property. func RefSchema(name string) *Schema { return &Schema{SchemaProps: SchemaProps{Ref: MustCreateRef(name)}} } -// ArrayProperty creates an array property +// ArrayProperty creates an array property. func ArrayProperty(items *Schema) *Schema { if items == nil { return &Schema{SchemaProps: SchemaProps{Type: []string{"array"}}} @@ -100,17 +102,17 @@ func ArrayProperty(items *Schema) *Schema { return &Schema{SchemaProps: SchemaProps{Items: &SchemaOrArray{Schema: items}, Type: []string{"array"}}} } -// ComposedSchema creates a schema with allOf +// ComposedSchema creates a schema with allOf. func ComposedSchema(schemas ...Schema) *Schema { s := new(Schema) s.AllOf = schemas return s } -// SchemaURL represents a schema url +// SchemaURL represents a schema url. type SchemaURL string -// MarshalJSON marshal this to JSON +// MarshalJSON marshal this to JSON. func (r SchemaURL) MarshalJSON() ([]byte, error) { if r == "" { return []byte("{}"), nil @@ -119,7 +121,7 @@ func (r SchemaURL) MarshalJSON() ([]byte, error) { return json.Marshal(v) } -// UnmarshalJSON unmarshal this from JSON +// UnmarshalJSON unmarshal this from JSON. func (r *SchemaURL) UnmarshalJSON(data []byte) error { var v map[string]any if err := json.Unmarshal(data, &v); err != nil { @@ -145,7 +147,7 @@ func (r *SchemaURL) fromMap(v map[string]any) error { return nil } -// SchemaProps describes a JSON schema (draft 4) +// SchemaProps describes a JSON schema (draft 4). type SchemaProps struct { ID string `json:"id,omitempty"` Ref Ref `json:"-"` @@ -184,7 +186,7 @@ type SchemaProps struct { Definitions Definitions `json:"definitions,omitempty"` } -// SwaggerSchemaProps are additional properties supported by swagger schemas, but not JSON-schema (draft 4) +// SwaggerSchemaProps are additional properties supported by swagger schemas, but not JSON-schema (draft 4). type SwaggerSchemaProps struct { Discriminator string `json:"discriminator,omitempty"` ReadOnly bool `json:"readOnly,omitempty"` @@ -208,7 +210,7 @@ type Schema struct { ExtraProps map[string]any `json:"-"` } -// JSONLookup implements an interface to customize json pointer lookup +// JSONLookup implements an interface to customize json pointer lookup. func (s Schema) JSONLookup(token string) (any, error) { if ex, ok := s.Extensions[token]; ok { return &ex, nil @@ -226,31 +228,31 @@ func (s Schema) JSONLookup(token string) (any, error) { return r, err } -// WithID sets the id for this schema, allows for chaining +// WithID sets the id for this schema, allows for chaining. func (s *Schema) WithID(id string) *Schema { s.ID = id return s } -// WithTitle sets the title for this schema, allows for chaining +// WithTitle sets the title for this schema, allows for chaining. func (s *Schema) WithTitle(title string) *Schema { s.Title = title return s } -// WithDescription sets the description for this schema, allows for chaining +// WithDescription sets the description for this schema, allows for chaining. func (s *Schema) WithDescription(description string) *Schema { s.Description = description return s } -// WithProperties sets the properties for this schema +// WithProperties sets the properties for this schema. func (s *Schema) WithProperties(schemas map[string]Schema) *Schema { s.Properties = schemas return s } -// SetProperty sets a property on this schema +// SetProperty sets a property on this schema. func (s *Schema) SetProperty(name string, schema Schema) *Schema { if s.Properties == nil { s.Properties = make(map[string]Schema) @@ -259,32 +261,32 @@ func (s *Schema) SetProperty(name string, schema Schema) *Schema { return s } -// WithAllOf sets the all of property +// WithAllOf sets the all of property. func (s *Schema) WithAllOf(schemas ...Schema) *Schema { s.AllOf = schemas return s } -// WithMaxProperties sets the max number of properties an object can have +// WithMaxProperties sets the max number of properties an object can have. func (s *Schema) WithMaxProperties(maximum int64) *Schema { s.MaxProperties = &maximum return s } -// WithMinProperties sets the min number of properties an object must have +// WithMinProperties sets the min number of properties an object must have. func (s *Schema) WithMinProperties(minimum int64) *Schema { s.MinProperties = &minimum return s } -// Typed sets the type of this schema for a single value item +// Typed sets the type of this schema for a single value item. func (s *Schema) Typed(tpe, format string) *Schema { s.Type = []string{tpe} s.Format = format return s } -// AddType adds a type with potential format to the types for this schema +// AddType adds a type with potential format to the types for this schema. func (s *Schema) AddType(tpe, format string) *Schema { s.Type = append(s.Type, tpe) if format != "" { @@ -299,124 +301,124 @@ func (s *Schema) AsNullable() *Schema { return s } -// CollectionOf a fluent builder method for an array parameter +// CollectionOf a fluent builder method for an array parameter. func (s *Schema) CollectionOf(items Schema) *Schema { s.Type = []string{jsonArray} s.Items = &SchemaOrArray{Schema: &items} return s } -// WithDefault sets the default value on this parameter +// WithDefault sets the default value on this parameter. func (s *Schema) WithDefault(defaultValue any) *Schema { s.Default = defaultValue return s } -// WithRequired flags this parameter as required +// WithRequired flags this parameter as required. func (s *Schema) WithRequired(items ...string) *Schema { s.Required = items return s } -// AddRequired adds field names to the required properties array +// AddRequired adds field names to the required properties array. func (s *Schema) AddRequired(items ...string) *Schema { s.Required = append(s.Required, items...) return s } -// WithMaxLength sets a max length value +// WithMaxLength sets a max length value. func (s *Schema) WithMaxLength(maximum int64) *Schema { s.MaxLength = &maximum return s } -// WithMinLength sets a min length value +// WithMinLength sets a min length value. func (s *Schema) WithMinLength(minimum int64) *Schema { s.MinLength = &minimum return s } -// WithPattern sets a pattern value +// WithPattern sets a pattern value. func (s *Schema) WithPattern(pattern string) *Schema { s.Pattern = pattern return s } -// WithMultipleOf sets a multiple of value +// WithMultipleOf sets a multiple of value. func (s *Schema) WithMultipleOf(number float64) *Schema { s.MultipleOf = &number return s } -// WithMaximum sets a maximum number value +// WithMaximum sets a maximum number value. func (s *Schema) WithMaximum(maximum float64, exclusive bool) *Schema { s.Maximum = &maximum s.ExclusiveMaximum = exclusive return s } -// WithMinimum sets a minimum number value +// WithMinimum sets a minimum number value. func (s *Schema) WithMinimum(minimum float64, exclusive bool) *Schema { s.Minimum = &minimum s.ExclusiveMinimum = exclusive return s } -// WithEnum sets a the enum values (replace) +// WithEnum sets a the enum values (replace). func (s *Schema) WithEnum(values ...any) *Schema { s.Enum = append([]any{}, values...) return s } -// WithMaxItems sets the max items +// WithMaxItems sets the max items. func (s *Schema) WithMaxItems(size int64) *Schema { s.MaxItems = &size return s } -// WithMinItems sets the min items +// WithMinItems sets the min items. func (s *Schema) WithMinItems(size int64) *Schema { s.MinItems = &size return s } -// UniqueValues dictates that this array can only have unique items +// UniqueValues dictates that this array can only have unique items. func (s *Schema) UniqueValues() *Schema { s.UniqueItems = true return s } -// AllowDuplicates this array can have duplicates +// AllowDuplicates this array can have duplicates. func (s *Schema) AllowDuplicates() *Schema { s.UniqueItems = false return s } -// AddToAllOf adds a schema to the allOf property +// AddToAllOf adds a schema to the allOf property. func (s *Schema) AddToAllOf(schemas ...Schema) *Schema { s.AllOf = append(s.AllOf, schemas...) return s } -// WithDiscriminator sets the name of the discriminator field +// WithDiscriminator sets the name of the discriminator field. func (s *Schema) WithDiscriminator(discriminator string) *Schema { s.Discriminator = discriminator return s } -// AsReadOnly flags this schema as readonly +// AsReadOnly flags this schema as readonly. func (s *Schema) AsReadOnly() *Schema { s.ReadOnly = true return s } -// AsWritable flags this schema as writeable (not read-only) +// AsWritable flags this schema as writeable (not read-only). func (s *Schema) AsWritable() *Schema { s.ReadOnly = false return s } -// WithExample sets the example for this schema +// WithExample sets the example for this schema. func (s *Schema) WithExample(example any) *Schema { s.Example = example return s @@ -440,7 +442,7 @@ func (s *Schema) WithExternalDocs(description, url string) *Schema { return s } -// WithXMLName sets the xml name for the object +// WithXMLName sets the xml name for the object. func (s *Schema) WithXMLName(name string) *Schema { if s.XML == nil { s.XML = new(XMLObject) @@ -449,7 +451,7 @@ func (s *Schema) WithXMLName(name string) *Schema { return s } -// WithXMLNamespace sets the xml namespace for the object +// WithXMLNamespace sets the xml namespace for the object. func (s *Schema) WithXMLNamespace(namespace string) *Schema { if s.XML == nil { s.XML = new(XMLObject) @@ -458,7 +460,7 @@ func (s *Schema) WithXMLNamespace(namespace string) *Schema { return s } -// WithXMLPrefix sets the xml prefix for the object +// WithXMLPrefix sets the xml prefix for the object. func (s *Schema) WithXMLPrefix(prefix string) *Schema { if s.XML == nil { s.XML = new(XMLObject) @@ -467,7 +469,7 @@ func (s *Schema) WithXMLPrefix(prefix string) *Schema { return s } -// AsXMLAttribute flags this object as xml attribute +// AsXMLAttribute flags this object as xml attribute. func (s *Schema) AsXMLAttribute() *Schema { if s.XML == nil { s.XML = new(XMLObject) @@ -476,7 +478,7 @@ func (s *Schema) AsXMLAttribute() *Schema { return s } -// AsXMLElement flags this object as an xml node +// AsXMLElement flags this object as an xml node. func (s *Schema) AsXMLElement() *Schema { if s.XML == nil { s.XML = new(XMLObject) @@ -485,7 +487,7 @@ func (s *Schema) AsXMLElement() *Schema { return s } -// AsWrappedXML flags this object as wrapped, this is mostly useful for array types +// AsWrappedXML flags this object as wrapped, this is mostly useful for array types. func (s *Schema) AsWrappedXML() *Schema { if s.XML == nil { s.XML = new(XMLObject) @@ -494,7 +496,7 @@ func (s *Schema) AsWrappedXML() *Schema { return s } -// AsUnwrappedXML flags this object as an xml node +// AsUnwrappedXML flags this object as an xml node. func (s *Schema) AsUnwrappedXML() *Schema { if s.XML == nil { s.XML = new(XMLObject) @@ -524,13 +526,13 @@ func (s *Schema) SetValidations(val SchemaValidations) { s.PatternProperties = val.PatternProperties } -// WithValidations is a fluent method to set schema validations +// WithValidations is a fluent method to set schema validations. func (s *Schema) WithValidations(val SchemaValidations) *Schema { s.SetValidations(val) return s } -// Validations returns a clone of the validations for this schema +// Validations returns a clone of the validations for this schema. func (s Schema) Validations() SchemaValidations { return SchemaValidations{ CommonValidations: CommonValidations{ @@ -553,40 +555,40 @@ func (s Schema) Validations() SchemaValidations { } } -// MarshalJSON marshal this to JSON +// MarshalJSON marshal this to JSON. func (s Schema) MarshalJSON() ([]byte, error) { b1, err := json.Marshal(s.SchemaProps) if err != nil { - return nil, fmt.Errorf("schema props %v: %w", err, ErrSpec) + 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: %w", err, ErrSpec) + 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: %w", err, ErrSpec) + 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: %w", err, ErrSpec) + 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: %w", err, ErrSpec) + 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: %w", err, ErrSpec) + return nil, fmt.Errorf("extra props %w: %w", err, ErrSpec) } b6 = jj } return jsonutils.ConcatJSON(b1, b2, b3, b4, b5, b6), nil } -// UnmarshalJSON marshal this from JSON +// UnmarshalJSON marshal this from JSON. func (s *Schema) UnmarshalJSON(data []byte) error { props := struct { SchemaProps diff --git a/schema_loader.go b/schema_loader.go index 8d4a9853..0894c932 100644 --- a/schema_loader.go +++ b/schema_loader.go @@ -24,7 +24,7 @@ import ( // NOTE: if you are using the go-openapi/loads package, it will override // 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) { +var PathLoader = func(pth string) (json.RawMessage, error) { //nolint:gochecknoglobals // package-level default loader, overridable by go-openapi/loads data, err := loading.LoadFromFileOrHTTP(pth) if err != nil { return nil, err @@ -76,7 +76,7 @@ type schemaLoader struct { // // 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 +// 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) } @@ -136,7 +136,7 @@ func (r *schemaLoader) resolveRef(ref *Ref, target any, basePath string) error { root := r.root if (ref.IsRoot() || ref.HasFragmentOnly) && root == nil && basePath != "" { if baseRef, erb := NewRef(basePath); erb == nil { - root, _, _, _ = r.load(baseRef.GetURL()) + root, _ = r.load(baseRef.GetURL()) } } @@ -144,7 +144,7 @@ func (r *schemaLoader) resolveRef(ref *Ref, target any, basePath string) error { data = root } else { baseRef := normalizeRef(ref, basePath) - data, _, _, err = r.load(baseRef.GetURL()) + data, err = r.load(baseRef.GetURL()) if err != nil { return err } @@ -160,33 +160,32 @@ func (r *schemaLoader) resolveRef(ref *Ref, target any, basePath string) error { return jsonutils.FromDynamicJSON(res, target) } -func (r *schemaLoader) load(refURL *url.URL) (any, url.URL, bool, error) { +func (r *schemaLoader) load(refURL *url.URL) (any, error) { debugLog("loading schema from url: %s", refURL) toFetch := *refURL toFetch.Fragment = "" - var err error pth := toFetch.String() normalized := normalizeBase(pth) debugLog("loading doc from: %s", normalized) data, fromCache := r.cache.Get(normalized) if fromCache { - return data, toFetch, fromCache, nil + return data, nil } b, err := r.context.loadDoc(normalized) if err != nil { - return nil, url.URL{}, false, err + return nil, err } var doc any if err := json.Unmarshal(b, &doc); err != nil { - return nil, url.URL{}, false, err + return nil, err } r.cache.Set(normalized, doc) - return doc, toFetch, fromCache, nil + return doc, nil } // isCircular detects cycles in sequences of $ref. @@ -293,8 +292,8 @@ func defaultSchemaLoader( root any, expandOptions *ExpandOptions, cache ResolutionCache, - context *resolverContext) *schemaLoader { - + context *resolverContext, +) *schemaLoader { if expandOptions == nil { expandOptions = &ExpandOptions{} } diff --git a/schema_test.go b/schema_test.go index 1009fbed..7d296a5b 100644 --- a/schema_test.go +++ b/schema_test.go @@ -12,7 +12,7 @@ import ( "github.com/go-openapi/testify/v2/require" ) -var schema = Schema{ +var schema = Schema{ //nolint:gochecknoglobals // test fixture VendorExtensible: VendorExtensible{Extensions: map[string]any{"x-framework": "go-swagger"}}, SchemaProps: SchemaProps{ Ref: MustCreateRef("Cat"), @@ -68,6 +68,7 @@ var schema = Schema{ }, } +//nolint:gochecknoglobals // test fixture var schemaJSON = `{ "x-framework": "go-swagger", "$ref": "Cat", @@ -139,55 +140,53 @@ var schemaJSON = `{ ` func TestSchema(t *testing.T) { - - expected := map[string]any{} - _ = json.Unmarshal([]byte(schemaJSON), &expected) - b, err := json.Marshal(schema) - require.NoError(t, err) - - var actual map[string]any - require.NoError(t, json.Unmarshal(b, &actual)) - assert.Equal(t, expected, actual) + assert.JSONMarshalAsT(t, schemaJSON, schema) actual2 := Schema{} require.NoError(t, json.Unmarshal([]byte(schemaJSON), &actual2)) assert.Equal(t, schema.Ref, actual2.Ref) - assert.Equal(t, schema.Description, actual2.Description) + assert.EqualT(t, schema.Description, actual2.Description) assert.Equal(t, schema.Maximum, actual2.Maximum) assert.Equal(t, schema.Minimum, actual2.Minimum) - assert.Equal(t, schema.ExclusiveMinimum, actual2.ExclusiveMinimum) - assert.Equal(t, schema.ExclusiveMaximum, actual2.ExclusiveMaximum) + assert.EqualT(t, schema.ExclusiveMinimum, actual2.ExclusiveMinimum) + assert.EqualT(t, schema.ExclusiveMaximum, actual2.ExclusiveMaximum) assert.Equal(t, schema.MaxLength, actual2.MaxLength) assert.Equal(t, schema.MinLength, actual2.MinLength) - assert.Equal(t, schema.Pattern, actual2.Pattern) + assert.EqualT(t, schema.Pattern, actual2.Pattern) assert.Equal(t, schema.MaxItems, actual2.MaxItems) assert.Equal(t, schema.MinItems, actual2.MinItems) - assert.True(t, actual2.UniqueItems) + assert.TrueT(t, actual2.UniqueItems) assert.Equal(t, schema.MultipleOf, actual2.MultipleOf) assert.Equal(t, schema.Enum, actual2.Enum) assert.Equal(t, schema.Type, actual2.Type) - assert.Equal(t, schema.Format, actual2.Format) - assert.Equal(t, schema.Title, actual2.Title) + assert.EqualT(t, schema.Format, actual2.Format) + assert.EqualT(t, schema.Title, actual2.Title) assert.Equal(t, schema.MaxProperties, actual2.MaxProperties) assert.Equal(t, schema.MinProperties, actual2.MinProperties) assert.Equal(t, schema.Required, actual2.Required) assert.Equal(t, schema.Items, actual2.Items) assert.Equal(t, schema.AllOf, actual2.AllOf) assert.Equal(t, schema.Properties, actual2.Properties) - assert.Equal(t, schema.Discriminator, actual2.Discriminator) - assert.Equal(t, schema.ReadOnly, actual2.ReadOnly) + assert.EqualT(t, schema.Discriminator, actual2.Discriminator) + assert.EqualT(t, schema.ReadOnly, actual2.ReadOnly) assert.Equal(t, schema.XML, actual2.XML) assert.Equal(t, schema.ExternalDocs, actual2.ExternalDocs) assert.Equal(t, schema.AdditionalProperties, actual2.AdditionalProperties) assert.Equal(t, schema.Extensions, actual2.Extensions) - examples := actual2.Example.([]any) - expEx := schema.Example.([]any) - ex1 := examples[0].(map[string]any) - ex2 := examples[1].(map[string]any) - exp1 := expEx[0].(map[string]any) - exp2 := expEx[1].(map[string]any) + examples, ok := actual2.Example.([]any) + require.TrueT(t, ok, "expected []any for actual2.Example") + expEx, ok := schema.Example.([]any) + require.TrueT(t, ok, "expected []any for schema.Example") + ex1, ok := examples[0].(map[string]any) + require.TrueT(t, ok, "expected map[string]any for examples[0]") + ex2, ok := examples[1].(map[string]any) + require.TrueT(t, ok, "expected map[string]any for examples[1]") + exp1, ok := expEx[0].(map[string]any) + require.TrueT(t, ok, "expected map[string]any for expEx[0]") + exp2, ok := expEx[1].(map[string]any) + require.TrueT(t, ok, "expected map[string]any for expEx[1]") assert.EqualValues(t, exp1["id"], ex1["id"]) assert.Equal(t, exp1["name"], ex1["name"]) diff --git a/schemas/v2/README.md b/schemas/v2/README.md index 32c1b929..af4656e7 100644 --- a/schemas/v2/README.md +++ b/schemas/v2/README.md @@ -2,4 +2,4 @@ This folder contains the Swagger 2.0 specification schema files maintained here: -https://github.com/reverb/swagger-spec/blob/master/schemas/v2.0 \ No newline at end of file + diff --git a/security_scheme.go b/security_scheme.go index 46a4a7e2..6d9019e7 100644 --- a/security_scheme.go +++ b/security_scheme.go @@ -20,17 +20,17 @@ const ( accessCode = "accessCode" ) -// BasicAuth creates a basic auth security scheme +// BasicAuth creates a basic auth security scheme. func BasicAuth() *SecurityScheme { return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{Type: basic}} } -// APIKeyAuth creates an api key auth security scheme +// APIKeyAuth creates an api key auth security scheme. func APIKeyAuth(fieldName, valueSource string) *SecurityScheme { return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{Type: apiKey, Name: fieldName, In: valueSource}} } -// OAuth2Implicit creates an implicit flow oauth2 security scheme +// OAuth2Implicit creates an implicit flow oauth2 security scheme. func OAuth2Implicit(authorizationURL string) *SecurityScheme { return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{ Type: oauth2, @@ -39,7 +39,7 @@ func OAuth2Implicit(authorizationURL string) *SecurityScheme { }} } -// OAuth2Password creates a password flow oauth2 security scheme +// OAuth2Password creates a password flow oauth2 security scheme. func OAuth2Password(tokenURL string) *SecurityScheme { return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{ Type: oauth2, @@ -48,7 +48,7 @@ func OAuth2Password(tokenURL string) *SecurityScheme { }} } -// OAuth2Application creates an application flow oauth2 security scheme +// OAuth2Application creates an application flow oauth2 security scheme. func OAuth2Application(tokenURL string) *SecurityScheme { return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{ Type: oauth2, @@ -57,7 +57,7 @@ func OAuth2Application(tokenURL string) *SecurityScheme { }} } -// OAuth2AccessToken creates an access token flow oauth2 security scheme +// OAuth2AccessToken creates an access token flow oauth2 security scheme. func OAuth2AccessToken(authorizationURL, tokenURL string) *SecurityScheme { return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{ Type: oauth2, @@ -67,7 +67,7 @@ func OAuth2AccessToken(authorizationURL, tokenURL string) *SecurityScheme { }} } -// SecuritySchemeProps describes a swagger security scheme in the securityDefinitions section +// SecuritySchemeProps describes a swagger security scheme in the securityDefinitions section. type SecuritySchemeProps struct { Description string `json:"description,omitempty"` Type string `json:"type"` @@ -79,7 +79,7 @@ type SecuritySchemeProps struct { Scopes map[string]string `json:"scopes,omitempty"` // oauth2 } -// AddScope adds a scope to this security scheme +// AddScope adds a scope to this security scheme. func (s *SecuritySchemeProps) AddScope(scope, description string) { if s.Scopes == nil { s.Scopes = make(map[string]string) @@ -97,7 +97,7 @@ type SecurityScheme struct { SecuritySchemeProps } -// JSONLookup implements an interface to customize json pointer lookup +// JSONLookup implements an interface to customize json pointer lookup. func (s SecurityScheme) JSONLookup(token string) (any, error) { if ex, ok := s.Extensions[token]; ok { return &ex, nil @@ -107,7 +107,7 @@ func (s SecurityScheme) JSONLookup(token string) (any, error) { return r, err } -// MarshalJSON marshal this to JSON +// MarshalJSON marshal this to JSON. func (s SecurityScheme) MarshalJSON() ([]byte, error) { var ( b1 []byte @@ -150,7 +150,7 @@ func (s SecurityScheme) MarshalJSON() ([]byte, error) { return jsonutils.ConcatJSON(b1, b2), nil } -// UnmarshalJSON marshal this from JSON +// UnmarshalJSON marshal this from JSON. func (s *SecurityScheme) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &s.SecuritySchemeProps); err != nil { return err diff --git a/spec.go b/spec.go index 0d0aaabc..4eba04b2 100644 --- a/spec.go +++ b/spec.go @@ -13,13 +13,13 @@ 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#" ) -// MustLoadJSONSchemaDraft04 panics when Swagger20Schema returns an error +// MustLoadJSONSchemaDraft04 panics when Swagger20Schema returns an error. func MustLoadJSONSchemaDraft04() *Schema { d, e := JSONSchemaDraft04() if e != nil { @@ -28,7 +28,7 @@ func MustLoadJSONSchemaDraft04() *Schema { return d } -// JSONSchemaDraft04 loads the json schema document for json schema draft04 +// JSONSchemaDraft04 loads the json schema document for json schema draft04. func JSONSchemaDraft04() (*Schema, error) { b, err := jsonschemaDraft04JSONBytes() if err != nil { @@ -42,7 +42,7 @@ func JSONSchemaDraft04() (*Schema, error) { return schema, nil } -// MustLoadSwagger20Schema panics when Swagger20Schema returns an error +// MustLoadSwagger20Schema panics when Swagger20Schema returns an error. func MustLoadSwagger20Schema() *Schema { d, e := Swagger20Schema() if e != nil { @@ -51,9 +51,8 @@ func MustLoadSwagger20Schema() *Schema { return d } -// Swagger20Schema loads the swagger 2.0 schema from the embedded assets +// 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 998c7c76..05976478 100644 --- a/spec_test.go +++ b/spec_test.go @@ -41,7 +41,7 @@ func TestSpec_Issue2743(t *testing.T) { require.NoError(t, spec.ExpandSpec(sp, &spec.ExpandOptions{RelativeBase: path, SkipSchemas: false, PathLoader: testLoader}), ) - require.NotContainsf(t, asJSON(t, sp), "$ref", "all $ref's should have been expanded properly") + require.StringNotContainsTf(t, asJSON(t, sp), "$ref", "all $ref's should have been expanded properly") }) }) } @@ -55,7 +55,7 @@ func TestSpec_Issue1429(t *testing.T) { require.NoError(t, err) // assert well expanded - require.Truef(t, (sp.Paths != nil && sp.Paths.Paths != nil), "expected paths to be available in fixture") + require.TrueTf(t, (sp.Paths != nil && sp.Paths.Paths != nil), "expected paths to be available in fixture") assertPaths1429(t, sp) @@ -69,12 +69,12 @@ func TestSpec_Issue1429(t *testing.T) { require.NoError(t, err) // assert well resolved - require.Truef(t, (sp.Paths != nil && sp.Paths.Paths != nil), "expected paths to be available in fixture") + require.TrueTf(t, (sp.Paths != nil && sp.Paths.Paths != nil), "expected paths to be available in fixture") assertPaths1429SkipSchema(t, sp) for _, def := range sp.Definitions { - assert.Contains(t, def.Ref.String(), "responses.yaml#/definitions/") + assert.StringContainsT(t, def.Ref.String(), "responses.yaml#/definitions/") } } @@ -112,14 +112,14 @@ func assertPaths1429SkipSchema(t testing.TB, sp *spec.Swagger) { continue case "nestedBody": // this one is local - assert.Truef(t, strings.HasPrefix(param.Schema.Ref.String(), "#/definitions/"), + assert.TrueTf(t, strings.HasPrefix(param.Schema.Ref.String(), "#/definitions/"), "expected rooted definitions $ref, got: %s", param.Schema.Ref.String()) continue case "remoteRequest": - assert.Contains(t, param.Schema.Ref.String(), "remote/remote.yaml#/") + assert.StringContainsT(t, param.Schema.Ref.String(), "remote/remote.yaml#/") continue } - assert.Contains(t, param.Schema.Ref.String(), "responses.yaml#/") + assert.StringContainsT(t, param.Schema.Ref.String(), "responses.yaml#/") } @@ -130,13 +130,13 @@ func assertPaths1429SkipSchema(t testing.TB, sp *spec.Swagger) { assert.Nilf(t, response.Schema, "expected response schema to be nil") continue case 204: - assert.Contains(t, response.Schema.Ref.String(), "remote/remote.yaml#/") + assert.StringContainsT(t, response.Schema.Ref.String(), "remote/remote.yaml#/") continue case 404: assert.Empty(t, response.Schema.Ref.String()) continue } - assert.Containsf(t, response.Schema.Ref.String(), "responses.yaml#/", "expected remote ref at resp. %d", code) + assert.StringContainsTf(t, response.Schema.Ref.String(), "responses.yaml#/", "expected remote ref at resp. %d", code) } } } @@ -149,7 +149,7 @@ func TestSpec_MoreLocalExpansion(t *testing.T) { require.NoError(t, spec.ExpandSpec(sp, &spec.ExpandOptions{RelativeBase: path, SkipSchemas: false, PathLoader: testLoader})) // asserts all $ref are expanded - assert.NotContains(t, asJSON(t, sp), `"$ref"`) + assert.StringNotContainsT(t, asJSON(t, sp), `"$ref"`) } func TestSpec_Issue69(t *testing.T) { diff --git a/structs_test.go b/structs_test.go index 684c4428..f917ff16 100644 --- a/structs_test.go +++ b/structs_test.go @@ -4,106 +4,51 @@ package spec import ( - "encoding/json" - "reflect" "testing" "github.com/go-openapi/testify/v2/assert" - yaml "go.yaml.in/yaml/v3" ) -func assertSerializeJSON(t testing.TB, actual any, expected string) bool { - ser, err := json.Marshal(actual) - if err != nil { - return assert.Failf(t, "unable to marshal to json", "got: %v: %#v", err, actual) - } - - return assert.Equal(t, expected, string(ser)) -} - -func assertSerializeYAML(t testing.TB, actual any, expected string) bool { - ser, err := yaml.Marshal(actual) - if err != nil { - return assert.Failf(t, "unable to marshal to yaml", "got: %v: %#v", err, actual) - } - return assert.Equal(t, expected, string(ser)) -} - -func derefTypeOf(expected any) (tpe reflect.Type) { - tpe = reflect.TypeOf(expected) - if tpe.Kind() == reflect.Ptr { - tpe = tpe.Elem() - } - return -} - -func isPointed(expected any) (pointed bool) { - tpe := reflect.TypeOf(expected) - if tpe.Kind() == reflect.Ptr { - pointed = true - } - return -} - -func assertParsesJSON(t testing.TB, actual string, expected any) bool { - parsed := reflect.New(derefTypeOf(expected)) - err := json.Unmarshal([]byte(actual), parsed.Interface()) - if err != nil { - return assert.Failf(t, "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) -} - -func assertParsesYAML(t testing.TB, actual string, expected any) bool { - parsed := reflect.New(derefTypeOf(expected)) - err := yaml.Unmarshal([]byte(actual), parsed.Interface()) - if err != nil { - return assert.Failf(t, "unable to unmarshal from yaml", "got: %v: %s", err, actual) - } - act := parsed.Interface() - if !isPointed(expected) { - act = reflect.Indirect(parsed).Interface() - } - return assert.Equal(t, expected, act) -} - func TestSerialization_SerializeJSON(t *testing.T) { - assertSerializeJSON(t, []string{"hello"}, "[\"hello\"]") - assertSerializeJSON(t, []string{"hello", "world", "and", "stuff"}, "[\"hello\",\"world\",\"and\",\"stuff\"]") - assertSerializeJSON(t, StringOrArray(nil), "null") - assertSerializeJSON(t, SchemaOrArray{ + assert.JSONMarshalAsT(t, `["hello"]`, []string{"hello"}) + assert.JSONMarshalAsT(t, `["hello","world","and","stuff"]`, []string{"hello", "world", "and", "stuff"}) + assert.JSONMarshalAsT(t, `null`, StringOrArray(nil)) + assert.JSONMarshalAsT(t, `[{"type":"string"}]`, SchemaOrArray{ Schemas: []Schema{ - {SchemaProps: SchemaProps{Type: []string{"string"}}}}, - }, "[{\"type\":\"string\"}]") - assertSerializeJSON(t, SchemaOrArray{ + {SchemaProps: SchemaProps{Type: []string{"string"}}}, + }, + }) + assert.JSONMarshalAsT(t, `[{"type":"string"},{"type":"string"}]`, SchemaOrArray{ Schemas: []Schema{ {SchemaProps: SchemaProps{Type: []string{"string"}}}, {SchemaProps: SchemaProps{Type: []string{"string"}}}, - }}, "[{\"type\":\"string\"},{\"type\":\"string\"}]") - assertSerializeJSON(t, SchemaOrArray{}, "null") + }, + }) + assert.JSONMarshalAsT(t, `null`, SchemaOrArray{}) } func TestSerialization_DeserializeJSON(t *testing.T) { // String - assertParsesJSON(t, "\"hello\"", StringOrArray([]string{"hello"})) - assertParsesJSON(t, "[\"hello\",\"world\",\"and\",\"stuff\"]", - StringOrArray([]string{"hello", "world", "and", "stuff"})) - assertParsesJSON(t, "[\"hello\",\"world\",null,\"stuff\"]", StringOrArray([]string{"hello", "world", "", "stuff"})) - assertParsesJSON(t, "null", StringOrArray(nil)) + assert.JSONUnmarshalAsT(t, StringOrArray([]string{"hello"}), `"hello"`) + assert.JSONUnmarshalAsT(t, + StringOrArray([]string{"hello", "world", "and", "stuff"}), + `["hello","world","and","stuff"]`) + assert.JSONUnmarshalAsT(t, + StringOrArray([]string{"hello", "world", "", "stuff"}), + `["hello","world",null,"stuff"]`) + assert.JSONUnmarshalAsT(t, StringOrArray(nil), `null`) // Schema - assertParsesJSON(t, "{\"type\":\"string\"}", SchemaOrArray{Schema: &Schema{ - SchemaProps: SchemaProps{Type: []string{"string"}}}, - }) - assertParsesJSON(t, "[{\"type\":\"string\"},{\"type\":\"string\"}]", &SchemaOrArray{ + assert.JSONUnmarshalAsT(t, SchemaOrArray{ + Schema: &Schema{ + SchemaProps: SchemaProps{Type: []string{"string"}}, + }, + }, `{"type":"string"}`) + assert.JSONUnmarshalAsT(t, &SchemaOrArray{ Schemas: []Schema{ {SchemaProps: SchemaProps{Type: []string{"string"}}}, {SchemaProps: SchemaProps{Type: []string{"string"}}}, }, - }) - assertParsesJSON(t, "null", SchemaOrArray{}) + }, `[{"type":"string"},{"type":"string"}]`) + assert.JSONUnmarshalAsT(t, SchemaOrArray{}, `null`) } diff --git a/swagger.go b/swagger.go index f7cd0f60..dbe32db8 100644 --- a/swagger.go +++ b/swagger.go @@ -25,7 +25,7 @@ type Swagger struct { SwaggerProps } -// JSONLookup look up a value by the json property name +// JSONLookup look up a value by the json property name. func (s Swagger) JSONLookup(token string) (any, error) { if ex, ok := s.Extensions[token]; ok { return &ex, nil @@ -34,7 +34,7 @@ func (s Swagger) JSONLookup(token string) (any, error) { return r, err } -// MarshalJSON marshals this swagger structure to json +// MarshalJSON marshals this swagger structure to json. func (s Swagger) MarshalJSON() ([]byte, error) { b1, err := json.Marshal(s.SwaggerProps) if err != nil { @@ -47,7 +47,7 @@ func (s Swagger) MarshalJSON() ([]byte, error) { return jsonutils.ConcatJSON(b1, b2), nil } -// UnmarshalJSON unmarshals a swagger spec from json +// UnmarshalJSON unmarshals a swagger spec from json. func (s *Swagger) UnmarshalJSON(data []byte) error { var sw Swagger if err := json.Unmarshal(data, &sw.SwaggerProps); err != nil { @@ -60,7 +60,7 @@ func (s *Swagger) UnmarshalJSON(data []byte) error { return nil } -// GobEncode provides a safe gob encoder for Swagger, including extensions +// GobEncode provides a safe gob encoder for Swagger, including extensions. func (s Swagger) GobEncode() ([]byte, error) { var b bytes.Buffer raw := struct { @@ -74,7 +74,7 @@ func (s Swagger) GobEncode() ([]byte, error) { return b.Bytes(), err } -// GobDecode provides a safe gob decoder for Swagger, including extensions +// GobDecode provides a safe gob decoder for Swagger, including extensions. func (s *Swagger) GobDecode(b []byte) error { var raw struct { Props SwaggerProps @@ -95,7 +95,7 @@ func (s *Swagger) GobDecode(b []byte) error { // NOTE: validation rules // - the scheme, when present must be from [http, https, ws, wss] // - BasePath must start with a leading "/" -// - Paths is required +// - Paths is required. type SwaggerProps struct { ID string `json:"id,omitempty"` Consumes []string `json:"consumes,omitempty"` @@ -126,7 +126,7 @@ type gobSwaggerPropsAlias struct { SecurityIsEmpty bool } -// GobEncode provides a safe gob encoder for SwaggerProps, including empty security requirements +// GobEncode provides a safe gob encoder for SwaggerProps, including empty security requirements. func (o SwaggerProps) GobEncode() ([]byte, error) { raw := gobSwaggerPropsAlias{ Alias: (*swaggerPropsAlias)(&o), @@ -171,7 +171,7 @@ func (o SwaggerProps) GobEncode() ([]byte, error) { return b.Bytes(), err } -// GobDecode provides a safe gob decoder for SwaggerProps, including empty security requirements +// GobDecode provides a safe gob decoder for SwaggerProps, including empty security requirements. func (o *SwaggerProps) GobDecode(b []byte) error { var raw gobSwaggerPropsAlias @@ -207,16 +207,16 @@ func (o *SwaggerProps) GobDecode(b []byte) error { return nil } -// Dependencies represent a dependencies property +// Dependencies represent a dependencies property. type Dependencies map[string]SchemaOrStringArray -// SchemaOrBool represents a schema or boolean value, is biased towards true for the boolean property +// SchemaOrBool represents a schema or boolean value, is biased towards true for the boolean property. type SchemaOrBool struct { Allows bool Schema *Schema } -// JSONLookup implements an interface to customize json pointer lookup +// JSONLookup implements an interface to customize json pointer lookup. func (s SchemaOrBool) JSONLookup(token string) (any, error) { if token == "allows" { return s.Allows, nil @@ -225,10 +225,12 @@ func (s SchemaOrBool) JSONLookup(token string) (any, error) { return r, err } -var jsTrue = []byte("true") -var jsFalse = []byte("false") +var ( + jsTrue = []byte("true") //nolint:gochecknoglobals // constant-like byte slices for JSON marshaling + jsFalse = []byte("false") //nolint:gochecknoglobals // constant-like byte slices for JSON marshaling +) -// MarshalJSON convert this object to JSON +// MarshalJSON convert this object to JSON. func (s SchemaOrBool) MarshalJSON() ([]byte, error) { if s.Schema != nil { return json.Marshal(s.Schema) @@ -240,7 +242,7 @@ func (s SchemaOrBool) MarshalJSON() ([]byte, error) { return jsTrue, nil } -// UnmarshalJSON converts this bool or schema object from a JSON structure +// UnmarshalJSON converts this bool or schema object from a JSON structure. func (s *SchemaOrBool) UnmarshalJSON(data []byte) error { var nw SchemaOrBool if len(data) > 0 { @@ -257,19 +259,19 @@ func (s *SchemaOrBool) UnmarshalJSON(data []byte) error { return nil } -// SchemaOrStringArray represents a schema or a string array +// SchemaOrStringArray represents a schema or a string array. type SchemaOrStringArray struct { Schema *Schema Property []string } -// JSONLookup implements an interface to customize json pointer lookup +// JSONLookup implements an interface to customize json pointer lookup. func (s SchemaOrStringArray) JSONLookup(token string) (any, error) { r, _, err := jsonpointer.GetForToken(s.Schema, token) return r, err } -// MarshalJSON converts this schema object or array into JSON structure +// MarshalJSON converts this schema object or array into JSON structure. func (s SchemaOrStringArray) MarshalJSON() ([]byte, error) { if len(s.Property) > 0 { return json.Marshal(s.Property) @@ -280,7 +282,7 @@ func (s SchemaOrStringArray) MarshalJSON() ([]byte, error) { return []byte("null"), nil } -// UnmarshalJSON converts this schema object or array from a JSON structure +// UnmarshalJSON converts this schema object or array from a JSON structure. func (s *SchemaOrStringArray) UnmarshalJSON(data []byte) error { var first byte if len(data) > 1 { @@ -318,15 +320,15 @@ type Definitions map[string]Schema type SecurityDefinitions map[string]*SecurityScheme // StringOrArray represents a value that can either be a string -// or an array of strings. Mainly here for serialization purposes +// or an array of strings. Mainly here for serialization purposes. type StringOrArray []string -// Contains returns true when the value is contained in the slice +// Contains returns true when the value is contained in the slice. func (s StringOrArray) Contains(value string) bool { return slices.Contains(s, value) } -// JSONLookup implements an interface to customize json pointer lookup +// JSONLookup implements an interface to customize json pointer lookup. func (s SchemaOrArray) JSONLookup(token string) (any, error) { if _, err := strconv.Atoi(token); err == nil { r, _, err := jsonpointer.GetForToken(s.Schemas, token) @@ -336,7 +338,7 @@ func (s SchemaOrArray) JSONLookup(token string) (any, error) { return r, err } -// UnmarshalJSON unmarshals this string or array object from a JSON array or JSON string +// UnmarshalJSON unmarshals this string or array object from a JSON array or JSON string. func (s *StringOrArray) UnmarshalJSON(data []byte) error { var first byte if len(data) > 1 { @@ -368,7 +370,7 @@ func (s *StringOrArray) UnmarshalJSON(data []byte) error { } } -// MarshalJSON converts this string or array to a JSON array or JSON string +// MarshalJSON converts this string or array to a JSON array or JSON string. func (s StringOrArray) MarshalJSON() ([]byte, error) { if len(s) == 1 { return json.Marshal([]string(s)[0]) @@ -377,13 +379,13 @@ func (s StringOrArray) MarshalJSON() ([]byte, error) { } // SchemaOrArray represents a value that can either be a Schema -// or an array of Schema. Mainly here for serialization purposes +// or an array of Schema. Mainly here for serialization purposes. type SchemaOrArray struct { Schema *Schema Schemas []Schema } -// Len returns the number of schemas in this property +// Len returns the number of schemas in this property. func (s SchemaOrArray) Len() int { if s.Schema != nil { return 1 @@ -391,7 +393,7 @@ func (s SchemaOrArray) Len() int { return len(s.Schemas) } -// ContainsType returns true when one of the schemas is of the specified type +// ContainsType returns true when one of the schemas is of the specified type. func (s *SchemaOrArray) ContainsType(name string) bool { if s.Schema != nil { return s.Schema.Type != nil && s.Schema.Type.Contains(name) @@ -399,7 +401,7 @@ func (s *SchemaOrArray) ContainsType(name string) bool { return false } -// MarshalJSON converts this schema object or array into JSON structure +// MarshalJSON converts this schema object or array into JSON structure. func (s SchemaOrArray) MarshalJSON() ([]byte, error) { if len(s.Schemas) > 0 { return json.Marshal(s.Schemas) @@ -407,7 +409,7 @@ func (s SchemaOrArray) MarshalJSON() ([]byte, error) { return json.Marshal(s.Schema) } -// UnmarshalJSON converts this schema object or array from a JSON structure +// UnmarshalJSON converts this schema object or array from a JSON structure. func (s *SchemaOrArray) UnmarshalJSON(data []byte) error { var nw SchemaOrArray var first byte diff --git a/swagger_test.go b/swagger_test.go index 5c167404..9ec0cad9 100644 --- a/swagger_test.go +++ b/swagger_test.go @@ -39,7 +39,7 @@ func init() { //nolint:gochecknoinits // it's okay to load embedded fixtures in } } -var spec = Swagger{ +var spec = Swagger{ //nolint:gochecknoglobals // test fixture SwaggerProps: SwaggerProps{ ID: "http://localhost:3849/api-docs", Swagger: "2.0", @@ -175,9 +175,10 @@ var spec = Swagger{ } */ -func assertSpecs(t testing.TB, actual, expected Swagger) bool { +func assertSpecs(t testing.TB, actual, expected Swagger) { + t.Helper() expected.Swagger = "2.0" - return assert.Equal(t, expected, actual) + assert.Equal(t, expected, actual) } /* @@ -225,25 +226,25 @@ func TestVendorExtensionStringSlice(t *testing.T) { var actual Swagger require.NoError(t, json.Unmarshal(specJSON, &actual)) schemes, ok := actual.Extensions.GetStringSlice("x-schemes") - require.True(t, ok) + require.TrueT(t, ok) assert.Equal(t, []string{"unix", "amqp"}, schemes) notSlice, ok := actual.Extensions.GetStringSlice("x-some-extension") assert.Nil(t, notSlice) - assert.False(t, ok) + assert.FalseT(t, ok) actual.AddExtension("x-another-ext", 100) notString, ok := actual.Extensions.GetStringSlice("x-another-ext") assert.Nil(t, notString) - assert.False(t, ok) + assert.FalseT(t, ok) 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) + assert.FalseT(t, ok) _, ok = actual.Extensions.GetStringSlice("x-notfound-ext") - assert.False(t, ok) + assert.FalseT(t, ok) } func TestOptionalSwaggerProps_Serialize(t *testing.T) { @@ -256,18 +257,18 @@ func TestOptionalSwaggerProps_Serialize(t *testing.T) { var ms map[string]any require.NoError(t, json.Unmarshal(bytes, &ms)) - assert.NotContains(t, ms, "consumes") - assert.NotContains(t, ms, "produces") - assert.NotContains(t, ms, "schemes") - assert.NotContains(t, ms, "host") - assert.NotContains(t, ms, "basePath") - assert.NotContains(t, ms, "definitions") - assert.NotContains(t, ms, "parameters") - assert.NotContains(t, ms, "responses") - assert.NotContains(t, ms, "securityDefinitions") - assert.NotContains(t, ms, "security") - assert.NotContains(t, ms, "tags") - assert.NotContains(t, ms, "externalDocs") + assert.MapNotContainsT(t, ms, "consumes") + assert.MapNotContainsT(t, ms, "produces") + assert.MapNotContainsT(t, ms, "schemes") + assert.MapNotContainsT(t, ms, "host") + assert.MapNotContainsT(t, ms, "basePath") + assert.MapNotContainsT(t, ms, "definitions") + assert.MapNotContainsT(t, ms, "parameters") + assert.MapNotContainsT(t, ms, "responses") + assert.MapNotContainsT(t, ms, "securityDefinitions") + assert.MapNotContainsT(t, ms, "security") + assert.MapNotContainsT(t, ms, "tags") + assert.MapNotContainsT(t, ms, "externalDocs") } func TestSecurityRequirements(t *testing.T) { @@ -276,11 +277,11 @@ func TestSecurityRequirements(t *testing.T) { sec := minimalSpec.Paths.Paths["/"].Get.Security require.Len(t, sec, 3) - assert.Contains(t, sec[0], "basic") - assert.Contains(t, sec[0], "apiKey") + assert.MapContainsT(t, sec[0], "basic") + assert.MapContainsT(t, sec[0], "apiKey") assert.NotNil(t, sec[1]) assert.Empty(t, sec[1]) - assert.Contains(t, sec[2], "queryKey") + assert.MapContainsT(t, sec[2], "queryKey") } func TestSwaggerGobEncoding(t *testing.T) { diff --git a/tag.go b/tag.go index ae98fd98..af3fb0a4 100644 --- a/tag.go +++ b/tag.go @@ -10,7 +10,7 @@ import ( "github.com/go-openapi/swag/jsonutils" ) -// TagProps describe a tag entry in the top level tags section of a swagger spec +// TagProps describe a tag entry in the top level tags section of a swagger spec. type TagProps struct { Description string `json:"description,omitempty"` Name string `json:"name,omitempty"` @@ -27,12 +27,12 @@ type Tag struct { TagProps } -// NewTag creates a new tag +// 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 +// JSONLookup implements an interface to customize json pointer lookup. func (t Tag) JSONLookup(token string) (any, error) { if ex, ok := t.Extensions[token]; ok { return &ex, nil @@ -42,7 +42,7 @@ func (t Tag) JSONLookup(token string) (any, error) { return r, err } -// MarshalJSON marshal this to JSON +// MarshalJSON marshal this to JSON. func (t Tag) MarshalJSON() ([]byte, error) { b1, err := json.Marshal(t.TagProps) if err != nil { @@ -55,7 +55,7 @@ func (t Tag) MarshalJSON() ([]byte, error) { return jsonutils.ConcatJSON(b1, b2), nil } -// UnmarshalJSON marshal this from JSON +// UnmarshalJSON marshal this from JSON. func (t *Tag) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &t.TagProps); err != nil { return err diff --git a/validations.go b/validations.go index 2c0dc424..a82c2ffe 100644 --- a/validations.go +++ b/validations.go @@ -3,7 +3,7 @@ package spec -// CommonValidations describe common JSON-schema validations +// CommonValidations describe common JSON-schema validations. type CommonValidations struct { Maximum *float64 `json:"maximum,omitempty"` ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"` @@ -143,22 +143,22 @@ func (v CommonValidations) Validations() SchemaValidations { } } -// HasNumberValidations indicates if the validations are for numbers or integers +// HasNumberValidations indicates if the validations are for numbers or integers. func (v CommonValidations) HasNumberValidations() bool { return v.Maximum != nil || v.Minimum != nil || v.MultipleOf != nil } -// HasStringValidations indicates if the validations are for strings +// HasStringValidations indicates if the validations are for strings. func (v CommonValidations) HasStringValidations() bool { return v.MaxLength != nil || v.MinLength != nil || v.Pattern != "" } -// HasArrayValidations indicates if the validations are for arrays +// HasArrayValidations indicates if the validations are for arrays. func (v CommonValidations) HasArrayValidations() bool { return v.MaxItems != nil || v.MinItems != nil || v.UniqueItems } -// HasEnum indicates if the validation includes some enum constraint +// HasEnum indicates if the validation includes some enum constraint. func (v CommonValidations) HasEnum() bool { return len(v.Enum) > 0 } @@ -175,12 +175,12 @@ type SchemaValidations struct { MinProperties *int64 `json:"minProperties,omitempty"` } -// HasObjectValidations indicates if the validations are for objects +// HasObjectValidations indicates if the validations are for objects. func (v SchemaValidations) HasObjectValidations() bool { return v.MaxProperties != nil || v.MinProperties != nil || v.PatternProperties != nil } -// SetValidations for schema validations +// SetValidations for schema validations. func (v *SchemaValidations) SetValidations(val SchemaValidations) { v.CommonValidations.SetValidations(val) v.PatternProperties = val.PatternProperties @@ -188,7 +188,7 @@ func (v *SchemaValidations) SetValidations(val SchemaValidations) { v.MinProperties = val.MinProperties } -// Validations for a schema +// Validations for a schema. func (v SchemaValidations) Validations() SchemaValidations { val := v.CommonValidations.Validations() val.PatternProperties = v.PatternProperties diff --git a/validations_test.go b/validations_test.go index 64422f41..21404cbb 100644 --- a/validations_test.go +++ b/validations_test.go @@ -36,7 +36,6 @@ func mkVal() SchemaValidations { } func TestValidations(t *testing.T) { - var cv CommonValidations val := mkVal() cv.SetValidations(val) @@ -44,34 +43,34 @@ func TestValidations(t *testing.T) { expectedCV := val.CommonValidations require.Equal(t, expectedCV, cv) - require.True(t, cv.HasArrayValidations()) - require.True(t, cv.HasNumberValidations()) - require.True(t, cv.HasStringValidations()) - require.True(t, cv.HasEnum()) + require.TrueT(t, cv.HasArrayValidations()) + require.TrueT(t, cv.HasNumberValidations()) + require.TrueT(t, cv.HasStringValidations()) + require.TrueT(t, cv.HasEnum()) cv.Enum = nil - require.False(t, cv.HasEnum()) + require.FalseT(t, cv.HasEnum()) cv.MaxLength = nil - require.True(t, cv.HasStringValidations()) + require.TrueT(t, cv.HasStringValidations()) cv.MinLength = nil - require.True(t, cv.HasStringValidations()) + require.TrueT(t, cv.HasStringValidations()) cv.Pattern = "" - require.False(t, cv.HasStringValidations()) + require.FalseT(t, cv.HasStringValidations()) cv.Minimum = nil - require.True(t, cv.HasNumberValidations()) + require.TrueT(t, cv.HasNumberValidations()) cv.Maximum = nil - require.True(t, cv.HasNumberValidations()) + require.TrueT(t, cv.HasNumberValidations()) cv.MultipleOf = nil - require.False(t, cv.HasNumberValidations()) + require.FalseT(t, cv.HasNumberValidations()) cv.MaxItems = nil - require.True(t, cv.HasArrayValidations()) + require.TrueT(t, cv.HasArrayValidations()) cv.MinItems = nil - require.True(t, cv.HasArrayValidations()) + require.TrueT(t, cv.HasArrayValidations()) cv.UniqueItems = false - require.False(t, cv.HasArrayValidations()) + require.FalseT(t, cv.HasArrayValidations()) val = mkVal() expectedSV := val @@ -92,24 +91,24 @@ func TestValidations(t *testing.T) { require.Equal(t, val, sv.Validations()) - require.True(t, sv.HasObjectValidations()) + require.TrueT(t, sv.HasObjectValidations()) sv.MinProperties = nil - require.True(t, sv.HasObjectValidations()) + require.TrueT(t, sv.HasObjectValidations()) sv.MaxProperties = nil - require.True(t, sv.HasObjectValidations()) + require.TrueT(t, sv.HasObjectValidations()) sv.PatternProperties = nil - require.False(t, sv.HasObjectValidations()) + require.FalseT(t, sv.HasObjectValidations()) val = mkVal() cv.SetValidations(val) cv.ClearStringValidations() - require.False(t, cv.HasStringValidations()) + require.FalseT(t, cv.HasStringValidations()) cv.ClearNumberValidations() - require.False(t, cv.HasNumberValidations()) + require.FalseT(t, cv.HasNumberValidations()) cv.ClearArrayValidations() - require.False(t, cv.HasArrayValidations()) + require.FalseT(t, cv.HasArrayValidations()) sv.SetValidations(val) sv.ClearObjectValidations(func(validation string, _ any) { @@ -121,5 +120,5 @@ func TestValidations(t *testing.T) { t.Fail() } }) - require.Falsef(t, sv.HasObjectValidations(), "%#v", sv) + require.FalseTf(t, sv.HasObjectValidations(), "%#v", sv) } diff --git a/xml_object.go b/xml_object.go index bf2f8f18..07f7ef8c 100644 --- a/xml_object.go +++ b/xml_object.go @@ -14,43 +14,43 @@ type XMLObject struct { Wrapped bool `json:"wrapped,omitempty"` } -// WithName sets the xml name for the object +// WithName sets the xml name for the object. func (x *XMLObject) WithName(name string) *XMLObject { x.Name = name return x } -// WithNamespace sets the xml namespace for the object +// WithNamespace sets the xml namespace for the object. func (x *XMLObject) WithNamespace(namespace string) *XMLObject { x.Namespace = namespace return x } -// WithPrefix sets the xml prefix for the object +// WithPrefix sets the xml prefix for the object. func (x *XMLObject) WithPrefix(prefix string) *XMLObject { x.Prefix = prefix return x } -// AsAttribute flags this object as xml attribute +// AsAttribute flags this object as xml attribute. func (x *XMLObject) AsAttribute() *XMLObject { x.Attribute = true return x } -// AsElement flags this object as an xml node +// AsElement flags this object as an xml node. func (x *XMLObject) AsElement() *XMLObject { x.Attribute = false return x } -// AsWrapped flags this object as wrapped, this is mostly useful for array types +// AsWrapped flags this object as wrapped, this is mostly useful for array types. func (x *XMLObject) AsWrapped() *XMLObject { x.Wrapped = true return x } -// AsUnwrapped flags this object as an xml node +// AsUnwrapped flags this object as an xml node. func (x *XMLObject) AsUnwrapped() *XMLObject { x.Wrapped = false return x diff --git a/xml_object_test.go b/xml_object_test.go index f0c46ba9..e1063eb0 100644 --- a/xml_object_test.go +++ b/xml_object_test.go @@ -15,7 +15,7 @@ func TestXmlObject_Serialize(t *testing.T) { obj1 := XMLObject{} actual, err := json.Marshal(obj1) require.NoError(t, err) - assert.Equal(t, "{}", string(actual)) + assert.EqualT(t, "{}", string(actual)) obj2 := XMLObject{ Name: "the name", @@ -33,15 +33,19 @@ func TestXmlObject_Serialize(t *testing.T) { assert.Equal(t, obj2.Name, ad["name"]) assert.Equal(t, obj2.Namespace, ad["namespace"]) assert.Equal(t, obj2.Prefix, ad["prefix"]) - assert.True(t, ad["attribute"].(bool)) - assert.True(t, ad["wrapped"].(bool)) + attrVal, ok := ad["attribute"].(bool) + require.TrueT(t, ok, "expected bool for attribute") + assert.TrueT(t, attrVal) + wrappedVal, ok := ad["wrapped"].(bool) + require.TrueT(t, ok, "expected bool for wrapped") + assert.TrueT(t, wrappedVal) } func TestXmlObject_Deserialize(t *testing.T) { expected := XMLObject{} actual := XMLObject{} require.NoError(t, json.Unmarshal([]byte("{}"), &actual)) - assert.Equal(t, expected, actual) + assert.EqualT(t, expected, actual) completed := `{"name":"the name","namespace":"the namespace","prefix":"the prefix","attribute":true,"wrapped":true}` expected = XMLObject{ @@ -54,5 +58,5 @@ func TestXmlObject_Deserialize(t *testing.T) { actual = XMLObject{} require.NoError(t, json.Unmarshal([]byte(completed), &actual)) - assert.Equal(t, expected, actual) + assert.EqualT(t, expected, actual) }