go.mod: update osbuild/images to v0.168.0

tag v0.165.0
Tagger: imagebuilder-bot <imagebuilder-bots+imagebuilder-bot@redhat.com>

Changes with 0.165.0

----------------
  *  distro: move rhel9 into a generic distro (osbuild/images#1645)
    * Author: Michael Vogt, Reviewers: Achilleas Koutsou, Simon de Vlieger
  * Revert "distro: drop `ImageType.BasePartitionTable()`" (osbuild/images#1691)
    * Author: Michael Vogt, Reviewers: Simon de Vlieger, Tomáš Hozza
  * Update dependencies 2025-07-20 (osbuild/images#1675)
    * Author: SchutzBot, Reviewers: Achilleas Koutsou, Simon de Vlieger
  * defs: add missing `bootstrap_containers` (osbuild/images#1679)
    * Author: Michael Vogt, Reviewers: Simon de Vlieger, Tomáš Hozza
  * disk: handle adding `PReP` partition on PPC64/s390x (HMS-8884) (osbuild/images#1681)
    * Author: Michael Vogt, Reviewers: Achilleas Koutsou, Simon de Vlieger
  * distro: bring per-distro checkOptions back (osbuild/images#1678)
    * Author: Michael Vogt, Reviewers: Simon de Vlieger, Tomáš Hozza
  * distro: cleanups in the pkg/distro/generic area (osbuild/images#1686)
    * Author: Michael Vogt, Reviewers: Achilleas Koutsou, Simon de Vlieger
  * distro: move rhel8 into a generic distro (osbuild/images#1643)
    * Author: Michael Vogt, Reviewers: Nobody
  * distro: small followups for PR#1682 (osbuild/images#1689)
    * Author: Michael Vogt, Reviewers: Achilleas Koutsou, Simon de Vlieger, Tomáš Hozza
  * distro: unify transform/match into a single concept (osbuild/images#1682)
    * Author: Michael Vogt, Reviewers: Achilleas Koutsou, Tomáš Hozza
  * distros: de-duplicate runner build packages for centos10 (osbuild/images#1680)
    * Author: Michael Vogt, Reviewers: Simon de Vlieger, Tomáš Hozza
  * github: disable Go dep updates through dependabot (osbuild/images#1683)
    * Author: Achilleas Koutsou, Reviewers: Simon de Vlieger, Tomáš Hozza
  * repos: include almalinux 9.6 (osbuild/images#1677)
    * Author: Simon de Vlieger, Reviewers: Lukáš Zapletal, Tomáš Hozza
  * rhel9: wsl distribution config (osbuild/images#1694)
    * Author: Simon de Vlieger, Reviewers: Michael Vogt, Sanne Raymaekers
  * test/manifests/all-customizations: don't embed local file via URI (osbuild/images#1684)
    * Author: Tomáš Hozza, Reviewers: Achilleas Koutsou, Brian C. Lane

— Somewhere on the Internet, 2025-07-28

---

tag v0.166.0
Tagger: imagebuilder-bot <imagebuilder-bots+imagebuilder-bot@redhat.com>

Changes with 0.166.0

----------------
  * customizations/subscription: conditionally enable semanage call (HMS-8866) (osbuild/images#1673)
    * Author: Sanne Raymaekers, Reviewers: Achilleas Koutsou, Michael Vogt
  * distro/rhel-10: versionlock shim-x64 in the azure-cvm image  (osbuild/images#1697)
    * Author: Achilleas Koutsou, Reviewers: Michael Vogt, Simon de Vlieger
  * manifestmock: move container/pkg/commit mocks into helper (osbuild/images#1700)
    * Author: Michael Vogt, Reviewers: Achilleas Koutsou, Simon de Vlieger
  * rhel9: `vagrant-libvirt`, `vagrant-virtualbox` (osbuild/images#1693)
    * Author: Simon de Vlieger, Reviewers: Michael Vogt, Sanne Raymaekers
  * rhel{9,10}: centos WSL refinement (HMS-8922) (osbuild/images#1690)
    * Author: Simon de Vlieger, Reviewers: Ondřej Budai, Sanne Raymaekers, Tomáš Hozza

— Somewhere on the Internet, 2025-07-29

---

tag v0.167.0
Tagger: imagebuilder-bot <imagebuilder-bots+imagebuilder-bot@redhat.com>

Changes with 0.167.0

----------------
  * RHEL/Azure: drop obsolete WAAgentConfig keys [RHEL-93894] and remove loglevel kernel option [RHEL-102372] (osbuild/images#1611)
    * Author: Achilleas Koutsou, Reviewers: Michael Vogt, Ondřej Budai, Sanne Raymaekers
  * Update dependencies 2025-07-27 (osbuild/images#1699)
    * Author: SchutzBot, Reviewers: Achilleas Koutsou, Simon de Vlieger
  * distro/rhel9: set default_kernel to kernel-uki-virt (osbuild/images#1704)
    * Author: Achilleas Koutsou, Reviewers: Ondřej Budai, Simon de Vlieger
  * distro: drop legacy loaders and update tests (osbuild/images#1687)
    * Author: Michael Vogt, Reviewers: Achilleas Koutsou, Tomáš Hozza
  * distro: fix issues with yaml distro definitions and enable yaml checks (osbuild/images#1702)
    * Author: Achilleas Koutsou, Reviewers: Michael Vogt, Ondřej Budai, Simon de Vlieger

— Somewhere on the Internet, 2025-07-30

---

tag v0.168.0
Tagger: imagebuilder-bot <imagebuilder-bots+imagebuilder-bot@redhat.com>

Changes with 0.168.0

----------------
  * distro: fix bug in variable substitution for static distros (osbuild/images#1710)
    * Author: Michael Vogt, Reviewers: Achilleas Koutsou, Simon de Vlieger
  * rhel{9,10}: azure for non-RHEL (HMS-8949) (osbuild/images#1707)
    * Author: Simon de Vlieger, Reviewers: Achilleas Koutsou, Michael Vogt

— Somewhere on the Internet, 2025-07-30

---
This commit is contained in:
Achilleas Koutsou 2025-07-30 15:46:51 +02:00
parent fad3b35d49
commit 6497b7520d
856 changed files with 72834 additions and 136836 deletions

View file

@ -1,5 +0,0 @@
coverage:
status:
patch:
default:
target: 80%

View file

@ -1,2 +0,0 @@
*.go text eol=lf

View file

@ -1,5 +0,0 @@
secrets.yml
coverage.out
coverage.txt
*.cov
.idea

View file

@ -1,61 +0,0 @@
linters-settings:
govet:
check-shadowing: true
golint:
min-confidence: 0
gocyclo:
min-complexity: 45
maligned:
suggest-new: true
dupl:
threshold: 200
goconst:
min-len: 2
min-occurrences: 3
linters:
enable-all: true
disable:
- maligned
- unparam
- lll
- gochecknoinits
- gochecknoglobals
- funlen
- godox
- gocognit
- whitespace
- wsl
- wrapcheck
- testpackage
- nlreturn
- gomnd
- exhaustivestruct
- goerr113
- errorlint
- nestif
- godot
- gofumpt
- paralleltest
- tparallel
- thelper
- ifshort
- exhaustruct
- varnamelen
- gci
- depguard
- errchkjson
- inamedparam
- nonamedreturns
- musttag
- ireturn
- forcetypeassert
- cyclop
# deprecated linters
- deadcode
- interfacer
- scopelint
- varcheck
- structcheck
- golint
- nosnakecase

View file

@ -1,74 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
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
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
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
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.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
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]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View file

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1,27 +0,0 @@
# OpenAPI analysis [![Build Status](https://github.com/go-openapi/analysis/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/analysis/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/analysis/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/analysis)
[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/analysis/master/LICENSE)
[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/analysis.svg)](https://pkg.go.dev/github.com/go-openapi/analysis)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/analysis)](https://goreportcard.com/report/github.com/go-openapi/analysis)
A foundational library to analyze an OAI specification document for easier reasoning about the content.
## What's inside?
* An analyzer providing methods to walk the functional content of a specification
* A spec flattener producing a self-contained document bundle, while preserving `$ref`s
* A spec merger ("mixin") to merge several spec documents into a primary spec
* A spec "fixer" ensuring that response descriptions are non empty
[Documentation](https://pkg.go.dev/github.com/go-openapi/analysis)
## FAQ
* Does this library support OpenAPI 3?
> No.
> This package currently only supports OpenAPI 2.0 (aka Swagger 2.0).
> 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.

File diff suppressed because it is too large Load diff

View file

@ -1,23 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package analysis
import (
"os"
"github.com/go-openapi/analysis/internal/debug"
)
var debugLog = debug.GetLogger("analysis", os.Getenv("SWAGGER_DEBUG") != "")

View file

@ -1,43 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
Package analysis provides methods to work with a Swagger specification document from
package go-openapi/spec.
## Analyzing a specification
An analysed specification object (type Spec) provides methods to work with swagger definition.
## Flattening or expanding a specification
Flattening a specification bundles all remote $ref in the main spec document.
Depending on flattening options, additional preprocessing may take place:
- full flattening: replacing all inline complex constructs by a named entry in #/definitions
- expand: replace all $ref's in the document by their expanded content
## Merging several specifications
Mixin several specifications merges all Swagger constructs, and warns about found conflicts.
## Fixing a specification
Unmarshalling a specification with golang json unmarshalling may lead to
some unwanted result on present but empty fields.
## Analyzing a Swagger schema
Swagger schemas are analyzed to determine their complexity and qualify their content.
*/
package analysis

View file

@ -1,79 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package analysis
import "github.com/go-openapi/spec"
// FixEmptyResponseDescriptions replaces empty ("") response
// descriptions in the input with "(empty)" to ensure that the
// resulting Swagger is stays valid. The problem appears to arise
// from reading in valid specs that have a explicit response
// description of "" (valid, response.description is required), but
// due to zero values being omitted upon re-serializing (omitempty) we
// lose them unless we stick some chars in there.
func FixEmptyResponseDescriptions(s *spec.Swagger) {
for k, v := range s.Responses {
FixEmptyDesc(&v) //#nosec
s.Responses[k] = v
}
if s.Paths == nil {
return
}
for _, v := range s.Paths.Paths {
if v.Get != nil {
FixEmptyDescs(v.Get.Responses)
}
if v.Put != nil {
FixEmptyDescs(v.Put.Responses)
}
if v.Post != nil {
FixEmptyDescs(v.Post.Responses)
}
if v.Delete != nil {
FixEmptyDescs(v.Delete.Responses)
}
if v.Options != nil {
FixEmptyDescs(v.Options.Responses)
}
if v.Head != nil {
FixEmptyDescs(v.Head.Responses)
}
if v.Patch != nil {
FixEmptyDescs(v.Patch.Responses)
}
}
}
// FixEmptyDescs adds "(empty)" as the description for any Response in
// the given Responses object that doesn't already have one.
func FixEmptyDescs(rs *spec.Responses) {
FixEmptyDesc(rs.Default)
for k, v := range rs.StatusCodeResponses {
FixEmptyDesc(&v) //#nosec
rs.StatusCodeResponses[k] = v
}
}
// FixEmptyDesc adds "(empty)" as the description to the given
// Response object if it doesn't already have one and isn't a
// ref. No-op on nil input.
func FixEmptyDesc(rs *spec.Response) {
if rs == nil || rs.Description != "" || rs.Ref.Ref.GetURL() != nil {
return
}
rs.Description = "(empty)"
}

View file

@ -1,814 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package analysis
import (
"fmt"
"log"
"path"
"sort"
"strings"
"github.com/go-openapi/analysis/internal/flatten/normalize"
"github.com/go-openapi/analysis/internal/flatten/operations"
"github.com/go-openapi/analysis/internal/flatten/replace"
"github.com/go-openapi/analysis/internal/flatten/schutils"
"github.com/go-openapi/analysis/internal/flatten/sortref"
"github.com/go-openapi/jsonpointer"
"github.com/go-openapi/spec"
)
const definitionsPath = "#/definitions"
// newRef stores information about refs created during the flattening process
type newRef struct {
key string
newName string
path string
isOAIGen bool
resolved bool
schema *spec.Schema
parents []string
}
// context stores intermediary results from flatten
type context struct {
newRefs map[string]*newRef
warnings []string
resolved map[string]string
}
func newContext() *context {
return &context{
newRefs: make(map[string]*newRef, 150),
warnings: make([]string, 0),
resolved: make(map[string]string, 50),
}
}
// Flatten an analyzed spec and produce a self-contained spec bundle.
//
// There is a minimal and a full flattening mode.
//
// Minimally flattening a spec means:
// - Expanding parameters, responses, path items, parameter items and header items (references to schemas are left
// unscathed)
// - Importing external (http, file) references so they become internal to the document
// - Moving every JSON pointer to a $ref to a named definition (i.e. the reworked spec does not contain pointers
// like "$ref": "#/definitions/myObject/allOfs/1")
//
// A minimally flattened spec thus guarantees the following properties:
// - all $refs point to a local definition (i.e. '#/definitions/...')
// - definitions are unique
//
// NOTE: arbitrary JSON pointers (other than $refs to top level definitions) are rewritten as definitions if they
// represent a complex schema or express commonality in the spec.
// Otherwise, they are simply expanded.
// Self-referencing JSON pointers cannot resolve to a type and trigger an error.
//
// Minimal flattening is necessary and sufficient for codegen rendering using go-swagger.
//
// Fully flattening a spec means:
// - Moving every complex inline schema to be a definition with an auto-generated name in a depth-first fashion.
//
// By complex, we mean every JSON object with some properties.
// Arrays, when they do not define a tuple,
// or empty objects with or without additionalProperties, are not considered complex and remain inline.
//
// NOTE: rewritten schemas get a vendor extension x-go-gen-location so we know from which part of the spec definitions
// have been created.
//
// Available flattening options:
// - Minimal: stops flattening after minimal $ref processing, leaving schema constructs untouched
// - Expand: expand all $ref's in the document (inoperant if Minimal set to true)
// - Verbose: croaks about name conflicts detected
// - RemoveUnused: removes unused parameters, responses and definitions after expansion/flattening
//
// NOTE: expansion removes all $ref save circular $ref, which remain in place
//
// TODO: additional options
// - ProgagateNameExtensions: ensure that created entries properly follow naming rules when their parent have set a
// x-go-name extension
// - LiftAllOfs:
// - limit the flattening of allOf members when simple objects
// - merge allOf with validation only
// - merge allOf with extensions only
// - ...
func Flatten(opts FlattenOpts) error {
debugLog("FlattenOpts: %#v", opts)
opts.flattenContext = newContext()
// 1. Recursively expand responses, parameters, path items and items in simple schemas.
//
// This simplifies the spec and leaves only the $ref's in schema objects.
if err := expand(&opts); err != nil {
return err
}
// 2. Strip the current document from absolute $ref's that actually a in the root,
// so we can recognize them as proper definitions
//
// In particular, this works around issue go-openapi/spec#76: leading absolute file in $ref is stripped
if err := normalizeRef(&opts); err != nil {
return err
}
// 3. Optionally remove shared parameters and responses already expanded (now unused).
//
// Operation parameters (i.e. under paths) remain.
if opts.RemoveUnused {
removeUnusedShared(&opts)
}
// 4. Import all remote references.
if err := importReferences(&opts); err != nil {
return err
}
// 5. full flattening: rewrite inline schemas (schemas that aren't simple types or arrays or maps)
if !opts.Minimal && !opts.Expand {
if err := nameInlinedSchemas(&opts); err != nil {
return err
}
}
// 6. Rewrite JSON pointers other than $ref to named definitions
// and attempt to resolve conflicting names whenever possible.
if err := stripPointersAndOAIGen(&opts); err != nil {
return err
}
// 7. Strip the spec from unused definitions
if opts.RemoveUnused {
removeUnused(&opts)
}
// 8. Issue warning notifications, if any
opts.croak()
// TODO: simplify known schema patterns to flat objects with properties
// examples:
// - lift simple allOf object,
// - empty allOf with validation only or extensions only
// - rework allOf arrays
// - rework allOf additionalProperties
return nil
}
func expand(opts *FlattenOpts) error {
if err := spec.ExpandSpec(opts.Swagger(), opts.ExpandOpts(!opts.Expand)); err != nil {
return err
}
opts.Spec.reload() // re-analyze
return nil
}
// normalizeRef strips the current file from any absolute file $ref. This works around issue go-openapi/spec#76:
// leading absolute file in $ref is stripped
func normalizeRef(opts *FlattenOpts) error {
debugLog("normalizeRef")
altered := false
for k, w := range opts.Spec.references.allRefs {
if !strings.HasPrefix(w.String(), opts.BasePath+definitionsPath) { // may be a mix of / and \, depending on OS
continue
}
altered = true
debugLog("stripping absolute path for: %s", w.String())
// strip the base path from definition
if err := replace.UpdateRef(opts.Swagger(), k,
spec.MustCreateRef(path.Join(definitionsPath, path.Base(w.String())))); err != nil {
return err
}
}
if altered {
opts.Spec.reload() // re-analyze
}
return nil
}
func removeUnusedShared(opts *FlattenOpts) {
opts.Swagger().Parameters = nil
opts.Swagger().Responses = nil
opts.Spec.reload() // re-analyze
}
func importReferences(opts *FlattenOpts) error {
var (
imported bool
err error
)
for !imported && err == nil {
// iteratively import remote references until none left.
// This inlining deals with name conflicts by introducing auto-generated names ("OAIGen")
imported, err = importExternalReferences(opts)
opts.Spec.reload() // re-analyze
}
return err
}
// nameInlinedSchemas replaces every complex inline construct by a named definition.
func nameInlinedSchemas(opts *FlattenOpts) error {
debugLog("nameInlinedSchemas")
namer := &InlineSchemaNamer{
Spec: opts.Swagger(),
Operations: operations.AllOpRefsByRef(opts.Spec, nil),
flattenContext: opts.flattenContext,
opts: opts,
}
depthFirst := sortref.DepthFirst(opts.Spec.allSchemas)
for _, key := range depthFirst {
sch := opts.Spec.allSchemas[key]
if sch.Schema == nil || sch.Schema.Ref.String() != "" || sch.TopLevel {
continue
}
asch, err := Schema(SchemaOpts{Schema: sch.Schema, Root: opts.Swagger(), BasePath: opts.BasePath})
if err != nil {
return fmt.Errorf("schema analysis [%s]: %w", key, err)
}
if asch.isAnalyzedAsComplex() { // move complex schemas to definitions
if err := namer.Name(key, sch.Schema, asch); err != nil {
return err
}
}
}
opts.Spec.reload() // re-analyze
return nil
}
func removeUnused(opts *FlattenOpts) {
for removeUnusedSinglePass(opts) {
// continue until no unused definition remains
}
}
func removeUnusedSinglePass(opts *FlattenOpts) (hasRemoved bool) {
expected := make(map[string]struct{})
for k := range opts.Swagger().Definitions {
expected[path.Join(definitionsPath, jsonpointer.Escape(k))] = struct{}{}
}
for _, k := range opts.Spec.AllDefinitionReferences() {
delete(expected, k)
}
for k := range expected {
hasRemoved = true
debugLog("removing unused definition %s", path.Base(k))
if opts.Verbose {
log.Printf("info: removing unused definition: %s", path.Base(k))
}
delete(opts.Swagger().Definitions, path.Base(k))
}
opts.Spec.reload() // re-analyze
return hasRemoved
}
func importKnownRef(entry sortref.RefRevIdx, refStr, newName string, opts *FlattenOpts) error {
// rewrite ref with already resolved external ref (useful for cyclical refs):
// rewrite external refs to local ones
debugLog("resolving known ref [%s] to %s", refStr, newName)
for _, key := range entry.Keys {
if err := replace.UpdateRef(opts.Swagger(), key, spec.MustCreateRef(path.Join(definitionsPath, newName))); err != nil {
return err
}
}
return nil
}
func importNewRef(entry sortref.RefRevIdx, refStr string, opts *FlattenOpts) error {
var (
isOAIGen bool
newName string
)
debugLog("resolving schema from remote $ref [%s]", refStr)
sch, err := spec.ResolveRefWithBase(opts.Swagger(), &entry.Ref, opts.ExpandOpts(false))
if err != nil {
return fmt.Errorf("could not resolve schema: %w", err)
}
// at this stage only $ref analysis matters
partialAnalyzer := &Spec{
references: referenceAnalysis{},
patterns: patternAnalysis{},
enums: enumAnalysis{},
}
partialAnalyzer.reset()
partialAnalyzer.analyzeSchema("", sch, "/")
// now rewrite those refs with rebase
for key, ref := range partialAnalyzer.references.allRefs {
if err := replace.UpdateRef(sch, key, spec.MustCreateRef(normalize.RebaseRef(entry.Ref.String(), ref.String()))); err != nil {
return fmt.Errorf("failed to rewrite ref for key %q at %s: %w", key, entry.Ref.String(), err)
}
}
// generate a unique name - isOAIGen means that a naming conflict was resolved by changing the name
newName, isOAIGen = uniqifyName(opts.Swagger().Definitions, nameFromRef(entry.Ref, opts))
debugLog("new name for [%s]: %s - with name conflict:%t", strings.Join(entry.Keys, ", "), newName, isOAIGen)
opts.flattenContext.resolved[refStr] = newName
// rewrite the external refs to local ones
for _, key := range entry.Keys {
if err := replace.UpdateRef(opts.Swagger(), key,
spec.MustCreateRef(path.Join(definitionsPath, newName))); err != nil {
return err
}
// keep track of created refs
resolved := false
if _, ok := opts.flattenContext.newRefs[key]; ok {
resolved = opts.flattenContext.newRefs[key].resolved
}
debugLog("keeping track of ref: %s (%s), resolved: %t", key, newName, resolved)
opts.flattenContext.newRefs[key] = &newRef{
key: key,
newName: newName,
path: path.Join(definitionsPath, newName),
isOAIGen: isOAIGen,
resolved: resolved,
schema: sch,
}
}
// add the resolved schema to the definitions
schutils.Save(opts.Swagger(), newName, sch)
return nil
}
// importExternalReferences iteratively digs remote references and imports them into the main schema.
//
// At every iteration, new remotes may be found when digging deeper: they are rebased to the current schema before being imported.
//
// This returns true when no more remote references can be found.
func importExternalReferences(opts *FlattenOpts) (bool, error) {
debugLog("importExternalReferences")
groupedRefs := sortref.ReverseIndex(opts.Spec.references.schemas, opts.BasePath)
sortedRefStr := make([]string, 0, len(groupedRefs))
if opts.flattenContext == nil {
opts.flattenContext = newContext()
}
// sort $ref resolution to ensure deterministic name conflict resolution
for refStr := range groupedRefs {
sortedRefStr = append(sortedRefStr, refStr)
}
sort.Strings(sortedRefStr)
complete := true
for _, refStr := range sortedRefStr {
entry := groupedRefs[refStr]
if entry.Ref.HasFragmentOnly {
continue
}
complete = false
newName := opts.flattenContext.resolved[refStr]
if newName != "" {
if err := importKnownRef(entry, refStr, newName, opts); err != nil {
return false, err
}
continue
}
// resolve schemas
if err := importNewRef(entry, refStr, opts); err != nil {
return false, err
}
}
// maintains ref index entries
for k := range opts.flattenContext.newRefs {
r := opts.flattenContext.newRefs[k]
// update tracking with resolved schemas
if r.schema.Ref.String() != "" {
ref := spec.MustCreateRef(r.path)
sch, err := spec.ResolveRefWithBase(opts.Swagger(), &ref, opts.ExpandOpts(false))
if err != nil {
return false, fmt.Errorf("could not resolve schema: %w", err)
}
r.schema = sch
}
if r.path == k {
continue
}
// update tracking with renamed keys: got a cascade of refs
renamed := *r
renamed.key = r.path
opts.flattenContext.newRefs[renamed.path] = &renamed
// indirect ref
r.newName = path.Base(k)
r.schema = spec.RefSchema(r.path)
r.path = k
r.isOAIGen = strings.Contains(k, "OAIGen")
}
return complete, nil
}
// stripPointersAndOAIGen removes anonymous JSON pointers from spec and chain with name conflicts handler.
// This loops until the spec has no such pointer and all name conflicts have been reduced as much as possible.
func stripPointersAndOAIGen(opts *FlattenOpts) error {
// name all JSON pointers to anonymous documents
if err := namePointers(opts); err != nil {
return err
}
// remove unnecessary OAIGen ref (created when flattening external refs creates name conflicts)
hasIntroducedPointerOrInline, ers := stripOAIGen(opts)
if ers != nil {
return ers
}
// iterate as pointer or OAIGen resolution may introduce inline schemas or pointers
for hasIntroducedPointerOrInline {
if !opts.Minimal {
opts.Spec.reload() // re-analyze
if err := nameInlinedSchemas(opts); err != nil {
return err
}
}
if err := namePointers(opts); err != nil {
return err
}
// restrip and re-analyze
var err error
if hasIntroducedPointerOrInline, err = stripOAIGen(opts); err != nil {
return err
}
}
return nil
}
// stripOAIGen strips the spec from unnecessary OAIGen constructs, initially created to dedupe flattened definitions.
//
// A dedupe is deemed unnecessary whenever:
// - the only conflict is with its (single) parent: OAIGen is merged into its parent (reinlining)
// - there is a conflict with multiple parents: merge OAIGen in first parent, the rewrite other parents to point to
// the first parent.
//
// This function returns true whenever it re-inlined a complex schema, so the caller may chose to iterate
// pointer and name resolution again.
func stripOAIGen(opts *FlattenOpts) (bool, error) {
debugLog("stripOAIGen")
replacedWithComplex := false
// figure out referers of OAIGen definitions (doing it before the ref start mutating)
for _, r := range opts.flattenContext.newRefs {
updateRefParents(opts.Spec.references.allRefs, r)
}
for k := range opts.flattenContext.newRefs {
r := opts.flattenContext.newRefs[k]
debugLog("newRefs[%s]: isOAIGen: %t, resolved: %t, name: %s, path:%s, #parents: %d, parents: %v, ref: %s",
k, r.isOAIGen, r.resolved, r.newName, r.path, len(r.parents), r.parents, r.schema.Ref.String())
if !r.isOAIGen || len(r.parents) == 0 {
continue
}
hasReplacedWithComplex, err := stripOAIGenForRef(opts, k, r)
if err != nil {
return replacedWithComplex, err
}
replacedWithComplex = replacedWithComplex || hasReplacedWithComplex
}
debugLog("replacedWithComplex: %t", replacedWithComplex)
opts.Spec.reload() // re-analyze
return replacedWithComplex, nil
}
// updateRefParents updates all parents of an updated $ref
func updateRefParents(allRefs map[string]spec.Ref, r *newRef) {
if !r.isOAIGen || r.resolved { // bail on already resolved entries (avoid looping)
return
}
for k, v := range allRefs {
if r.path != v.String() {
continue
}
found := false
for _, p := range r.parents {
if p == k {
found = true
break
}
}
if !found {
r.parents = append(r.parents, k)
}
}
}
func stripOAIGenForRef(opts *FlattenOpts, k string, r *newRef) (bool, error) {
replacedWithComplex := false
pr := sortref.TopmostFirst(r.parents)
// rewrite first parent schema in hierarchical then lexicographical order
debugLog("rewrite first parent %s with schema", pr[0])
if err := replace.UpdateRefWithSchema(opts.Swagger(), pr[0], r.schema); err != nil {
return false, err
}
if pa, ok := opts.flattenContext.newRefs[pr[0]]; ok && pa.isOAIGen {
// update parent in ref index entry
debugLog("update parent entry: %s", pr[0])
pa.schema = r.schema
pa.resolved = false
replacedWithComplex = true
}
// rewrite other parents to point to first parent
if len(pr) > 1 {
for _, p := range pr[1:] {
replacingRef := spec.MustCreateRef(pr[0])
// set complex when replacing ref is an anonymous jsonpointer: further processing may be required
replacedWithComplex = replacedWithComplex || path.Dir(replacingRef.String()) != definitionsPath
debugLog("rewrite parent with ref: %s", replacingRef.String())
// NOTE: it is possible at this stage to introduce json pointers (to non-definitions places).
// Those are stripped later on.
if err := replace.UpdateRef(opts.Swagger(), p, replacingRef); err != nil {
return false, err
}
if pa, ok := opts.flattenContext.newRefs[p]; ok && pa.isOAIGen {
// update parent in ref index
debugLog("update parent entry: %s", p)
pa.schema = r.schema
pa.resolved = false
replacedWithComplex = true
}
}
}
// remove OAIGen definition
debugLog("removing definition %s", path.Base(r.path))
delete(opts.Swagger().Definitions, path.Base(r.path))
// propagate changes in ref index for keys which have this one as a parent
for kk, value := range opts.flattenContext.newRefs {
if kk == k || !value.isOAIGen || value.resolved {
continue
}
found := false
newParents := make([]string, 0, len(value.parents))
for _, parent := range value.parents {
switch {
case parent == r.path:
found = true
parent = pr[0]
case strings.HasPrefix(parent, r.path+"/"):
found = true
parent = path.Join(pr[0], strings.TrimPrefix(parent, r.path))
}
newParents = append(newParents, parent)
}
if found {
value.parents = newParents
}
}
// mark naming conflict as resolved
debugLog("marking naming conflict resolved for key: %s", r.key)
opts.flattenContext.newRefs[r.key].isOAIGen = false
opts.flattenContext.newRefs[r.key].resolved = true
// determine if the previous substitution did inline a complex schema
if r.schema != nil && r.schema.Ref.String() == "" { // inline schema
asch, err := Schema(SchemaOpts{Schema: r.schema, Root: opts.Swagger(), BasePath: opts.BasePath})
if err != nil {
return false, err
}
debugLog("re-inlined schema: parent: %s, %t", pr[0], asch.isAnalyzedAsComplex())
replacedWithComplex = replacedWithComplex || !(path.Dir(pr[0]) == definitionsPath) && asch.isAnalyzedAsComplex()
}
return replacedWithComplex, nil
}
// namePointers replaces all JSON pointers to anonymous documents by a $ref to a new named definitions.
//
// This is carried on depth-first. Pointers to $refs which are top level definitions are replaced by the $ref itself.
// Pointers to simple types are expanded, unless they express commonality (i.e. several such $ref are used).
func namePointers(opts *FlattenOpts) error {
debugLog("name pointers")
refsToReplace := make(map[string]SchemaRef, len(opts.Spec.references.schemas))
for k, ref := range opts.Spec.references.allRefs {
debugLog("name pointers: %q => %#v", k, ref)
if path.Dir(ref.String()) == definitionsPath {
// this a ref to a top-level definition: ok
continue
}
result, err := replace.DeepestRef(opts.Swagger(), opts.ExpandOpts(false), ref)
if err != nil {
return fmt.Errorf("at %s, %w", k, err)
}
replacingRef := result.Ref
sch := result.Schema
if opts.flattenContext != nil {
opts.flattenContext.warnings = append(opts.flattenContext.warnings, result.Warnings...)
}
debugLog("planning pointer to replace at %s: %s, resolved to: %s", k, ref.String(), replacingRef.String())
refsToReplace[k] = SchemaRef{
Name: k, // caller
Ref: replacingRef, // called
Schema: sch,
TopLevel: path.Dir(replacingRef.String()) == definitionsPath,
}
}
depthFirst := sortref.DepthFirst(refsToReplace)
namer := &InlineSchemaNamer{
Spec: opts.Swagger(),
Operations: operations.AllOpRefsByRef(opts.Spec, nil),
flattenContext: opts.flattenContext,
opts: opts,
}
for _, key := range depthFirst {
v := refsToReplace[key]
// update current replacement, which may have been updated by previous changes of deeper elements
result, erd := replace.DeepestRef(opts.Swagger(), opts.ExpandOpts(false), v.Ref)
if erd != nil {
return fmt.Errorf("at %s, %w", key, erd)
}
if opts.flattenContext != nil {
opts.flattenContext.warnings = append(opts.flattenContext.warnings, result.Warnings...)
}
v.Ref = result.Ref
v.Schema = result.Schema
v.TopLevel = path.Dir(result.Ref.String()) == definitionsPath
debugLog("replacing pointer at %s: resolved to: %s", key, v.Ref.String())
if v.TopLevel {
debugLog("replace pointer %s by canonical definition: %s", key, v.Ref.String())
// if the schema is a $ref to a top level definition, just rewrite the pointer to this $ref
if err := replace.UpdateRef(opts.Swagger(), key, v.Ref); err != nil {
return err
}
continue
}
if err := flattenAnonPointer(key, v, refsToReplace, namer, opts); err != nil {
return err
}
}
opts.Spec.reload() // re-analyze
return nil
}
func flattenAnonPointer(key string, v SchemaRef, refsToReplace map[string]SchemaRef, namer *InlineSchemaNamer, opts *FlattenOpts) error {
// this is a JSON pointer to an anonymous document (internal or external):
// create a definition for this schema when:
// - it is a complex schema
// - or it is pointed by more than one $ref (i.e. expresses commonality)
// otherwise, expand the pointer (single reference to a simple type)
//
// The named definition for this follows the target's key, not the caller's
debugLog("namePointers at %s for %s", key, v.Ref.String())
// qualify the expanded schema
asch, ers := Schema(SchemaOpts{Schema: v.Schema, Root: opts.Swagger(), BasePath: opts.BasePath})
if ers != nil {
return fmt.Errorf("schema analysis [%s]: %w", key, ers)
}
callers := make([]string, 0, 64)
debugLog("looking for callers")
an := New(opts.Swagger())
for k, w := range an.references.allRefs {
r, err := replace.DeepestRef(opts.Swagger(), opts.ExpandOpts(false), w)
if err != nil {
return fmt.Errorf("at %s, %w", key, err)
}
if opts.flattenContext != nil {
opts.flattenContext.warnings = append(opts.flattenContext.warnings, r.Warnings...)
}
if r.Ref.String() == v.Ref.String() {
callers = append(callers, k)
}
}
debugLog("callers for %s: %d", v.Ref.String(), len(callers))
if len(callers) == 0 {
// has already been updated and resolved
return nil
}
parts := sortref.KeyParts(v.Ref.String())
debugLog("number of callers for %s: %d", v.Ref.String(), len(callers))
// identifying edge case when the namer did nothing because we point to a non-schema object
// no definition is created and we expand the $ref for all callers
debugLog("decide what to do with the schema pointed to: asch.IsSimpleSchema=%t, len(callers)=%d, parts.IsSharedParam=%t, parts.IsSharedResponse=%t",
asch.IsSimpleSchema, len(callers), parts.IsSharedParam(), parts.IsSharedResponse(),
)
if (!asch.IsSimpleSchema || len(callers) > 1) && !parts.IsSharedParam() && !parts.IsSharedResponse() {
debugLog("replace JSON pointer at [%s] by definition: %s", key, v.Ref.String())
if err := namer.Name(v.Ref.String(), v.Schema, asch); err != nil {
return err
}
// regular case: we named the $ref as a definition, and we move all callers to this new $ref
for _, caller := range callers {
if caller == key {
continue
}
// move $ref for next to resolve
debugLog("identified caller of %s at [%s]", v.Ref.String(), caller)
c := refsToReplace[caller]
c.Ref = v.Ref
refsToReplace[caller] = c
}
return nil
}
// everything that is a simple schema and not factorizable is expanded
debugLog("expand JSON pointer for key=%s", key)
if err := replace.UpdateRefWithSchema(opts.Swagger(), key, v.Schema); err != nil {
return err
}
// NOTE: there is no other caller to update
return nil
}

View file

@ -1,308 +0,0 @@
package analysis
import (
"fmt"
"path"
"sort"
"strings"
"github.com/go-openapi/analysis/internal/flatten/operations"
"github.com/go-openapi/analysis/internal/flatten/replace"
"github.com/go-openapi/analysis/internal/flatten/schutils"
"github.com/go-openapi/analysis/internal/flatten/sortref"
"github.com/go-openapi/spec"
"github.com/go-openapi/swag"
)
// InlineSchemaNamer finds a new name for an inlined type
type InlineSchemaNamer struct {
Spec *spec.Swagger
Operations map[string]operations.OpRef
flattenContext *context
opts *FlattenOpts
}
// Name yields a new name for the inline schema
func (isn *InlineSchemaNamer) Name(key string, schema *spec.Schema, aschema *AnalyzedSchema) error {
debugLog("naming inlined schema at %s", key)
parts := sortref.KeyParts(key)
for _, name := range namesFromKey(parts, aschema, isn.Operations) {
if name == "" {
continue
}
// create unique name
mangle := mangler(isn.opts)
newName, isOAIGen := uniqifyName(isn.Spec.Definitions, mangle(name))
// clone schema
sch := schutils.Clone(schema)
// replace values on schema
debugLog("rewriting schema to ref: key=%s with new name: %s", key, newName)
if err := replace.RewriteSchemaToRef(isn.Spec, key,
spec.MustCreateRef(path.Join(definitionsPath, newName))); err != nil {
return fmt.Errorf("error while creating definition %q from inline schema: %w", newName, err)
}
// rewrite any dependent $ref pointing to this place,
// when not already pointing to a top-level definition.
//
// NOTE: this is important if such referers use arbitrary JSON pointers.
an := New(isn.Spec)
for k, v := range an.references.allRefs {
r, erd := replace.DeepestRef(isn.opts.Swagger(), isn.opts.ExpandOpts(false), v)
if erd != nil {
return fmt.Errorf("at %s, %w", k, erd)
}
if isn.opts.flattenContext != nil {
isn.opts.flattenContext.warnings = append(isn.opts.flattenContext.warnings, r.Warnings...)
}
if r.Ref.String() != key && (r.Ref.String() != path.Join(definitionsPath, newName) || path.Dir(v.String()) == definitionsPath) {
continue
}
debugLog("found a $ref to a rewritten schema: %s points to %s", k, v.String())
// rewrite $ref to the new target
if err := replace.UpdateRef(isn.Spec, k,
spec.MustCreateRef(path.Join(definitionsPath, newName))); err != nil {
return err
}
}
// NOTE: this extension is currently not used by go-swagger (provided for information only)
sch.AddExtension("x-go-gen-location", GenLocation(parts))
// save cloned schema to definitions
schutils.Save(isn.Spec, newName, sch)
// keep track of created refs
if isn.flattenContext == nil {
continue
}
debugLog("track created ref: key=%s, newName=%s, isOAIGen=%t", key, newName, isOAIGen)
resolved := false
if _, ok := isn.flattenContext.newRefs[key]; ok {
resolved = isn.flattenContext.newRefs[key].resolved
}
isn.flattenContext.newRefs[key] = &newRef{
key: key,
newName: newName,
path: path.Join(definitionsPath, newName),
isOAIGen: isOAIGen,
resolved: resolved,
schema: sch,
}
}
return nil
}
// uniqifyName yields a unique name for a definition
func uniqifyName(definitions spec.Definitions, name string) (string, bool) {
isOAIGen := false
if name == "" {
name = "oaiGen"
isOAIGen = true
}
if len(definitions) == 0 {
return name, isOAIGen
}
unq := true
for k := range definitions {
if strings.EqualFold(k, name) {
unq = false
break
}
}
if unq {
return name, isOAIGen
}
name += "OAIGen"
isOAIGen = true
var idx int
unique := name
_, known := definitions[unique]
for known {
idx++
unique = fmt.Sprintf("%s%d", name, idx)
_, known = definitions[unique]
}
return unique, isOAIGen
}
func namesFromKey(parts sortref.SplitKey, aschema *AnalyzedSchema, operations map[string]operations.OpRef) []string {
var (
baseNames [][]string
startIndex int
)
switch {
case parts.IsOperation():
baseNames, startIndex = namesForOperation(parts, operations)
case parts.IsDefinition():
baseNames, startIndex = namesForDefinition(parts)
default:
// this a non-standard pointer: build a name by concatenating its parts
baseNames = [][]string{parts}
startIndex = len(baseNames) + 1
}
result := make([]string, 0, len(baseNames))
for _, segments := range baseNames {
nm := parts.BuildName(segments, startIndex, partAdder(aschema))
if nm == "" {
continue
}
result = append(result, nm)
}
sort.Strings(result)
debugLog("names from parts: %v => %v", parts, result)
return result
}
func namesForParam(parts sortref.SplitKey, operations map[string]operations.OpRef) ([][]string, int) {
var (
baseNames [][]string
startIndex int
)
piref := parts.PathItemRef()
if piref.String() != "" && parts.IsOperationParam() {
if op, ok := operations[piref.String()]; ok {
startIndex = 5
baseNames = append(baseNames, []string{op.ID, "params", "body"})
}
} else if parts.IsSharedOperationParam() {
pref := parts.PathRef()
for k, v := range operations {
if strings.HasPrefix(k, pref.String()) {
startIndex = 4
baseNames = append(baseNames, []string{v.ID, "params", "body"})
}
}
}
return baseNames, startIndex
}
func namesForOperation(parts sortref.SplitKey, operations map[string]operations.OpRef) ([][]string, int) {
var (
baseNames [][]string
startIndex int
)
// params
if parts.IsOperationParam() || parts.IsSharedOperationParam() {
baseNames, startIndex = namesForParam(parts, operations)
}
// responses
if parts.IsOperationResponse() {
piref := parts.PathItemRef()
if piref.String() != "" {
if op, ok := operations[piref.String()]; ok {
startIndex = 6
baseNames = append(baseNames, []string{op.ID, parts.ResponseName(), "body"})
}
}
}
return baseNames, startIndex
}
func namesForDefinition(parts sortref.SplitKey) ([][]string, int) {
nm := parts.DefinitionName()
if nm != "" {
return [][]string{{parts.DefinitionName()}}, 2
}
return [][]string{}, 0
}
// partAdder knows how to interpret a schema when it comes to build a name from parts
func partAdder(aschema *AnalyzedSchema) sortref.PartAdder {
return func(part string) []string {
segments := make([]string, 0, 2)
if part == "items" || part == "additionalItems" {
if aschema.IsTuple || aschema.IsTupleWithExtra {
segments = append(segments, "tuple")
} else {
segments = append(segments, "items")
}
if part == "additionalItems" {
segments = append(segments, part)
}
return segments
}
segments = append(segments, part)
return segments
}
}
func mangler(o *FlattenOpts) func(string) string {
if o.KeepNames {
return func(in string) string { return in }
}
return swag.ToJSONName
}
func nameFromRef(ref spec.Ref, o *FlattenOpts) string {
mangle := mangler(o)
u := ref.GetURL()
if u.Fragment != "" {
return mangle(path.Base(u.Fragment))
}
if u.Path != "" {
bn := path.Base(u.Path)
if bn != "" && bn != "/" {
ext := path.Ext(bn)
if ext != "" {
return mangle(bn[:len(bn)-len(ext)])
}
return mangle(bn)
}
}
return mangle(strings.ReplaceAll(u.Host, ".", " "))
}
// GenLocation indicates from which section of the specification (models or operations) a definition has been created.
//
// This is reflected in the output spec with a "x-go-gen-location" extension. At the moment, this is provided
// for information only.
func GenLocation(parts sortref.SplitKey) string {
switch {
case parts.IsOperation():
return "operations"
case parts.IsDefinition():
return "models"
default:
return ""
}
}

View file

@ -1,79 +0,0 @@
package analysis
import (
"log"
"github.com/go-openapi/spec"
)
// FlattenOpts configuration for flattening a swagger specification.
//
// The BasePath parameter is used to locate remote relative $ref found in the specification.
// This path is a file: it points to the location of the root document and may be either a local
// file path or a URL.
//
// If none specified, relative references (e.g. "$ref": "folder/schema.yaml#/definitions/...")
// found in the spec are searched from the current working directory.
type FlattenOpts struct {
Spec *Spec // The analyzed spec to work with
flattenContext *context // Internal context to track flattening activity
BasePath string // The location of the root document for this spec to resolve relative $ref
// Flattening options
Expand bool // When true, skip flattening the spec and expand it instead (if Minimal is false)
Minimal bool // When true, do not decompose complex structures such as allOf
Verbose bool // enable some reporting on possible name conflicts detected
RemoveUnused bool // When true, remove unused parameters, responses and definitions after expansion/flattening
ContinueOnError bool // Continue when spec expansion issues are found
KeepNames bool // Do not attempt to jsonify names from references when flattening
/* Extra keys */
_ struct{} // require keys
}
// ExpandOpts creates a spec.ExpandOptions to configure expanding a specification document.
func (f *FlattenOpts) ExpandOpts(skipSchemas bool) *spec.ExpandOptions {
return &spec.ExpandOptions{
RelativeBase: f.BasePath,
SkipSchemas: skipSchemas,
ContinueOnError: f.ContinueOnError,
}
}
// Swagger gets the swagger specification for this flatten operation
func (f *FlattenOpts) Swagger() *spec.Swagger {
return f.Spec.spec
}
// croak logs notifications and warnings about valid, but possibly unwanted constructs resulting
// from flattening a spec
func (f *FlattenOpts) croak() {
if !f.Verbose {
return
}
reported := make(map[string]bool, len(f.flattenContext.newRefs))
for _, v := range f.Spec.references.allRefs {
// warns about duplicate handling
for _, r := range f.flattenContext.newRefs {
if r.isOAIGen && r.path == v.String() {
reported[r.newName] = true
}
}
}
for k := range reported {
log.Printf("warning: duplicate flattened definition name resolved as %s", k)
}
// warns about possible type mismatches
uniqueMsg := make(map[string]bool)
for _, msg := range f.flattenContext.warnings {
if _, ok := uniqueMsg[msg]; ok {
continue
}
log.Printf("warning: %s", msg)
uniqueMsg[msg] = true
}
}

View file

@ -1,41 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package debug
import (
"fmt"
"log"
"os"
"path/filepath"
"runtime"
)
var (
output = os.Stdout
)
// GetLogger provides a prefix debug logger
func GetLogger(prefix string, debug bool) func(string, ...interface{}) {
if debug {
logger := log.New(output, prefix+":", log.LstdFlags)
return func(msg string, args ...interface{}) {
_, file1, pos1, _ := runtime.Caller(1)
logger.Printf("%s:%d: %s", filepath.Base(file1), pos1, fmt.Sprintf(msg, args...))
}
}
return func(_ string, _ ...interface{}) {}
}

View file

@ -1,87 +0,0 @@
package normalize
import (
"net/url"
"path"
"path/filepath"
"strings"
"github.com/go-openapi/spec"
)
// RebaseRef rebases a remote ref relative to a base ref.
//
// NOTE: does not support JSONschema ID for $ref (we assume we are working with swagger specs here).
//
// NOTE(windows):
// * refs are assumed to have been normalized with drive letter lower cased (from go-openapi/spec)
// * "/ in paths may appear as escape sequences
func RebaseRef(baseRef string, ref string) string {
baseRef, _ = url.PathUnescape(baseRef)
ref, _ = url.PathUnescape(ref)
if baseRef == "" || baseRef == "." || strings.HasPrefix(baseRef, "#") {
return ref
}
parts := strings.Split(ref, "#")
baseParts := strings.Split(baseRef, "#")
baseURL, _ := url.Parse(baseParts[0])
if strings.HasPrefix(ref, "#") {
if baseURL.Host == "" {
return strings.Join([]string{baseParts[0], parts[1]}, "#")
}
return strings.Join([]string{baseParts[0], parts[1]}, "#")
}
refURL, _ := url.Parse(parts[0])
if refURL.Host != "" || filepath.IsAbs(parts[0]) {
// not rebasing an absolute path
return ref
}
// there is a relative path
var basePath string
if baseURL.Host != "" {
// when there is a host, standard URI rules apply (with "/")
baseURL.Path = path.Dir(baseURL.Path)
baseURL.Path = path.Join(baseURL.Path, "/"+parts[0])
return baseURL.String()
}
// this is a local relative path
// basePart[0] and parts[0] are local filesystem directories/files
basePath = filepath.Dir(baseParts[0])
relPath := filepath.Join(basePath, string(filepath.Separator)+parts[0])
if len(parts) > 1 {
return strings.Join([]string{relPath, parts[1]}, "#")
}
return relPath
}
// Path renders absolute path on remote file refs
//
// NOTE(windows):
// * refs are assumed to have been normalized with drive letter lower cased (from go-openapi/spec)
// * "/ in paths may appear as escape sequences
func Path(ref spec.Ref, basePath string) string {
uri, _ := url.PathUnescape(ref.String())
if ref.HasFragmentOnly || filepath.IsAbs(uri) {
return uri
}
refURL, _ := url.Parse(uri)
if refURL.Host != "" {
return uri
}
parts := strings.Split(uri, "#")
// BasePath, parts[0] are local filesystem directories, guaranteed to be absolute at this stage
parts[0] = filepath.Join(filepath.Dir(basePath), parts[0])
return strings.Join(parts, "#")
}

View file

@ -1,90 +0,0 @@
package operations
import (
"path"
"sort"
"strings"
"github.com/go-openapi/jsonpointer"
"github.com/go-openapi/spec"
"github.com/go-openapi/swag"
)
// AllOpRefsByRef returns an index of sortable operations
func AllOpRefsByRef(specDoc Provider, operationIDs []string) map[string]OpRef {
return OpRefsByRef(GatherOperations(specDoc, operationIDs))
}
// OpRefsByRef indexes a map of sortable operations
func OpRefsByRef(oprefs map[string]OpRef) map[string]OpRef {
result := make(map[string]OpRef, len(oprefs))
for _, v := range oprefs {
result[v.Ref.String()] = v
}
return result
}
// OpRef is an indexable, sortable operation
type OpRef struct {
Method string
Path string
Key string
ID string
Op *spec.Operation
Ref spec.Ref
}
// OpRefs is a sortable collection of operations
type OpRefs []OpRef
func (o OpRefs) Len() int { return len(o) }
func (o OpRefs) Swap(i, j int) { o[i], o[j] = o[j], o[i] }
func (o OpRefs) Less(i, j int) bool { return o[i].Key < o[j].Key }
// Provider knows how to collect operations from a spec
type Provider interface {
Operations() map[string]map[string]*spec.Operation
}
// GatherOperations builds a map of sorted operations from a spec
func GatherOperations(specDoc Provider, operationIDs []string) map[string]OpRef {
var oprefs OpRefs
for method, pathItem := range specDoc.Operations() {
for pth, operation := range pathItem {
vv := *operation
oprefs = append(oprefs, OpRef{
Key: swag.ToGoName(strings.ToLower(method) + " " + pth),
Method: method,
Path: pth,
ID: vv.ID,
Op: &vv,
Ref: spec.MustCreateRef("#" + path.Join("/paths", jsonpointer.Escape(pth), method)),
})
}
}
sort.Sort(oprefs)
operations := make(map[string]OpRef)
for _, opr := range oprefs {
nm := opr.ID
if nm == "" {
nm = opr.Key
}
oo, found := operations[nm]
if found && oo.Method != opr.Method && oo.Path != opr.Path {
nm = opr.Key
}
if len(operationIDs) == 0 || swag.ContainsStrings(operationIDs, opr.ID) || swag.ContainsStrings(operationIDs, nm) {
opr.ID = nm
opr.Op.ID = nm
operations[nm] = opr
}
}
return operations
}

View file

@ -1,458 +0,0 @@
package replace
import (
"encoding/json"
"fmt"
"net/url"
"os"
"path"
"strconv"
"github.com/go-openapi/analysis/internal/debug"
"github.com/go-openapi/jsonpointer"
"github.com/go-openapi/spec"
)
const definitionsPath = "#/definitions"
var debugLog = debug.GetLogger("analysis/flatten/replace", os.Getenv("SWAGGER_DEBUG") != "")
// RewriteSchemaToRef replaces a schema with a Ref
func RewriteSchemaToRef(sp *spec.Swagger, key string, ref spec.Ref) error {
debugLog("rewriting schema to ref for %s with %s", key, ref.String())
_, value, err := getPointerFromKey(sp, key)
if err != nil {
return err
}
switch refable := value.(type) {
case *spec.Schema:
return rewriteParentRef(sp, key, ref)
case spec.Schema:
return rewriteParentRef(sp, key, ref)
case *spec.SchemaOrArray:
if refable.Schema != nil {
refable.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
}
case *spec.SchemaOrBool:
if refable.Schema != nil {
refable.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
}
case map[string]interface{}: // this happens e.g. if a schema points to an extension unmarshaled as map[string]interface{}
return rewriteParentRef(sp, key, ref)
default:
return fmt.Errorf("no schema with ref found at %s for %T", key, value)
}
return nil
}
func rewriteParentRef(sp *spec.Swagger, key string, ref spec.Ref) error {
parent, entry, pvalue, err := getParentFromKey(sp, key)
if err != nil {
return err
}
debugLog("rewriting holder for %T", pvalue)
switch container := pvalue.(type) {
case spec.Response:
if err := rewriteParentRef(sp, "#"+parent, ref); err != nil {
return err
}
case *spec.Response:
container.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
case *spec.Responses:
statusCode, err := strconv.Atoi(entry)
if err != nil {
return fmt.Errorf("%s not a number: %w", key[1:], err)
}
resp := container.StatusCodeResponses[statusCode]
resp.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
container.StatusCodeResponses[statusCode] = resp
case map[string]spec.Response:
resp := container[entry]
resp.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
container[entry] = resp
case spec.Parameter:
if err := rewriteParentRef(sp, "#"+parent, ref); err != nil {
return err
}
case map[string]spec.Parameter:
param := container[entry]
param.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
container[entry] = param
case []spec.Parameter:
idx, err := strconv.Atoi(entry)
if err != nil {
return fmt.Errorf("%s not a number: %w", key[1:], err)
}
param := container[idx]
param.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
container[idx] = param
case spec.Definitions:
container[entry] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
case map[string]spec.Schema:
container[entry] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
case []spec.Schema:
idx, err := strconv.Atoi(entry)
if err != nil {
return fmt.Errorf("%s not a number: %w", key[1:], err)
}
container[idx] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
case *spec.SchemaOrArray:
// NOTE: this is necessarily an array - otherwise, the parent would be *Schema
idx, err := strconv.Atoi(entry)
if err != nil {
return fmt.Errorf("%s not a number: %w", key[1:], err)
}
container.Schemas[idx] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
case spec.SchemaProperties:
container[entry] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
case *interface{}:
*container = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
// NOTE: can't have case *spec.SchemaOrBool = parent in this case is *Schema
default:
return fmt.Errorf("unhandled parent schema rewrite %s (%T)", key, pvalue)
}
return nil
}
// getPointerFromKey retrieves the content of the JSON pointer "key"
func getPointerFromKey(sp interface{}, key string) (string, interface{}, error) {
switch sp.(type) {
case *spec.Schema:
case *spec.Swagger:
default:
panic("unexpected type used in getPointerFromKey")
}
if key == "#/" {
return "", sp, nil
}
// unescape chars in key, e.g. "{}" from path params
pth, _ := url.PathUnescape(key[1:])
ptr, err := jsonpointer.New(pth)
if err != nil {
return "", nil, err
}
value, _, err := ptr.Get(sp)
if err != nil {
debugLog("error when getting key: %s with path: %s", key, pth)
return "", nil, err
}
return pth, value, nil
}
// getParentFromKey retrieves the container of the JSON pointer "key"
func getParentFromKey(sp interface{}, key string) (string, string, interface{}, error) {
switch sp.(type) {
case *spec.Schema:
case *spec.Swagger:
default:
panic("unexpected type used in getPointerFromKey")
}
// unescape chars in key, e.g. "{}" from path params
pth, _ := url.PathUnescape(key[1:])
parent, entry := path.Dir(pth), path.Base(pth)
debugLog("getting schema holder at: %s, with entry: %s", parent, entry)
pptr, err := jsonpointer.New(parent)
if err != nil {
return "", "", nil, err
}
pvalue, _, err := pptr.Get(sp)
if err != nil {
return "", "", nil, fmt.Errorf("can't get parent for %s: %w", parent, err)
}
return parent, entry, pvalue, nil
}
// UpdateRef replaces a ref by another one
func UpdateRef(sp interface{}, key string, ref spec.Ref) error {
switch sp.(type) {
case *spec.Schema:
case *spec.Swagger:
default:
panic("unexpected type used in getPointerFromKey")
}
debugLog("updating ref for %s with %s", key, ref.String())
pth, value, err := getPointerFromKey(sp, key)
if err != nil {
return err
}
switch refable := value.(type) {
case *spec.Schema:
refable.Ref = ref
case *spec.SchemaOrArray:
if refable.Schema != nil {
refable.Schema.Ref = ref
}
case *spec.SchemaOrBool:
if refable.Schema != nil {
refable.Schema.Ref = ref
}
case spec.Schema:
debugLog("rewriting holder for %T", refable)
_, entry, pvalue, erp := getParentFromKey(sp, key)
if erp != nil {
return err
}
switch container := pvalue.(type) {
case spec.Definitions:
container[entry] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
case map[string]spec.Schema:
container[entry] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
case []spec.Schema:
idx, err := strconv.Atoi(entry)
if err != nil {
return fmt.Errorf("%s not a number: %w", pth, err)
}
container[idx] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
case *spec.SchemaOrArray:
// NOTE: this is necessarily an array - otherwise, the parent would be *Schema
idx, err := strconv.Atoi(entry)
if err != nil {
return fmt.Errorf("%s not a number: %w", pth, err)
}
container.Schemas[idx] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
case spec.SchemaProperties:
container[entry] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
// NOTE: can't have case *spec.SchemaOrBool = parent in this case is *Schema
default:
return fmt.Errorf("unhandled container type at %s: %T", key, value)
}
default:
return fmt.Errorf("no schema with ref found at %s for %T", key, value)
}
return nil
}
// UpdateRefWithSchema replaces a ref with a schema (i.e. re-inline schema)
func UpdateRefWithSchema(sp *spec.Swagger, key string, sch *spec.Schema) error {
debugLog("updating ref for %s with schema", key)
pth, value, err := getPointerFromKey(sp, key)
if err != nil {
return err
}
switch refable := value.(type) {
case *spec.Schema:
*refable = *sch
case spec.Schema:
_, entry, pvalue, erp := getParentFromKey(sp, key)
if erp != nil {
return err
}
switch container := pvalue.(type) {
case spec.Definitions:
container[entry] = *sch
case map[string]spec.Schema:
container[entry] = *sch
case []spec.Schema:
idx, err := strconv.Atoi(entry)
if err != nil {
return fmt.Errorf("%s not a number: %w", pth, err)
}
container[idx] = *sch
case *spec.SchemaOrArray:
// NOTE: this is necessarily an array - otherwise, the parent would be *Schema
idx, err := strconv.Atoi(entry)
if err != nil {
return fmt.Errorf("%s not a number: %w", pth, err)
}
container.Schemas[idx] = *sch
case spec.SchemaProperties:
container[entry] = *sch
// NOTE: can't have case *spec.SchemaOrBool = parent in this case is *Schema
default:
return fmt.Errorf("unhandled type for parent of [%s]: %T", key, value)
}
case *spec.SchemaOrArray:
*refable.Schema = *sch
// NOTE: can't have case *spec.SchemaOrBool = parent in this case is *Schema
case *spec.SchemaOrBool:
*refable.Schema = *sch
default:
return fmt.Errorf("no schema with ref found at %s for %T", key, value)
}
return nil
}
// DeepestRefResult holds the results from DeepestRef analysis
type DeepestRefResult struct {
Ref spec.Ref
Schema *spec.Schema
Warnings []string
}
// DeepestRef finds the first definition ref, from a cascade of nested refs which are not definitions.
// - if no definition is found, returns the deepest ref.
// - pointers to external files are expanded
//
// NOTE: all external $ref's are assumed to be already expanded at this stage.
func DeepestRef(sp *spec.Swagger, opts *spec.ExpandOptions, ref spec.Ref) (*DeepestRefResult, error) {
if !ref.HasFragmentOnly {
// we found an external $ref, which is odd at this stage:
// do nothing on external $refs
return &DeepestRefResult{Ref: ref}, nil
}
currentRef := ref
visited := make(map[string]bool, 64)
warnings := make([]string, 0, 2)
DOWNREF:
for currentRef.String() != "" {
if path.Dir(currentRef.String()) == definitionsPath {
// this is a top-level definition: stop here and return this ref
return &DeepestRefResult{Ref: currentRef}, nil
}
if _, beenThere := visited[currentRef.String()]; beenThere {
return nil,
fmt.Errorf("cannot resolve cyclic chain of pointers under %s", currentRef.String())
}
visited[currentRef.String()] = true
value, _, err := currentRef.GetPointer().Get(sp)
if err != nil {
return nil, err
}
switch refable := value.(type) {
case *spec.Schema:
if refable.Ref.String() == "" {
break DOWNREF
}
currentRef = refable.Ref
case spec.Schema:
if refable.Ref.String() == "" {
break DOWNREF
}
currentRef = refable.Ref
case *spec.SchemaOrArray:
if refable.Schema == nil || refable.Schema != nil && refable.Schema.Ref.String() == "" {
break DOWNREF
}
currentRef = refable.Schema.Ref
case *spec.SchemaOrBool:
if refable.Schema == nil || refable.Schema != nil && refable.Schema.Ref.String() == "" {
break DOWNREF
}
currentRef = refable.Schema.Ref
case spec.Response:
// a pointer points to a schema initially marshalled in responses section...
// Attempt to convert this to a schema. If this fails, the spec is invalid
asJSON, _ := refable.MarshalJSON()
var asSchema spec.Schema
err := asSchema.UnmarshalJSON(asJSON)
if err != nil {
return nil,
fmt.Errorf("invalid type for resolved JSON pointer %s. Expected a schema a, got: %T (%v)",
currentRef.String(), value, err,
)
}
warnings = append(warnings, fmt.Sprintf("found $ref %q (response) interpreted as schema", currentRef.String()))
if asSchema.Ref.String() == "" {
break DOWNREF
}
currentRef = asSchema.Ref
case spec.Parameter:
// a pointer points to a schema initially marshalled in parameters section...
// Attempt to convert this to a schema. If this fails, the spec is invalid
asJSON, _ := refable.MarshalJSON()
var asSchema spec.Schema
if err := asSchema.UnmarshalJSON(asJSON); err != nil {
return nil,
fmt.Errorf("invalid type for resolved JSON pointer %s. Expected a schema a, got: %T (%v)",
currentRef.String(), value, err,
)
}
warnings = append(warnings, fmt.Sprintf("found $ref %q (parameter) interpreted as schema", currentRef.String()))
if asSchema.Ref.String() == "" {
break DOWNREF
}
currentRef = asSchema.Ref
default:
// fallback: attempts to resolve the pointer as a schema
if refable == nil {
break DOWNREF
}
asJSON, _ := json.Marshal(refable)
var asSchema spec.Schema
if err := asSchema.UnmarshalJSON(asJSON); err != nil {
return nil,
fmt.Errorf("unhandled type to resolve JSON pointer %s. Expected a Schema, got: %T (%v)",
currentRef.String(), value, err,
)
}
warnings = append(warnings, fmt.Sprintf("found $ref %q (%T) interpreted as schema", currentRef.String(), refable))
if asSchema.Ref.String() == "" {
break DOWNREF
}
currentRef = asSchema.Ref
}
}
// assess what schema we're ending with
sch, erv := spec.ResolveRefWithBase(sp, &currentRef, opts)
if erv != nil {
return nil, erv
}
if sch == nil {
return nil, fmt.Errorf("no schema found at %s", currentRef.String())
}
return &DeepestRefResult{Ref: currentRef, Schema: sch, Warnings: warnings}, nil
}

View file

@ -1,29 +0,0 @@
// Package schutils provides tools to save or clone a schema
// when flattening a spec.
package schutils
import (
"github.com/go-openapi/spec"
"github.com/go-openapi/swag"
)
// Save registers a schema as an entry in spec #/definitions
func Save(sp *spec.Swagger, name string, schema *spec.Schema) {
if schema == nil {
return
}
if sp.Definitions == nil {
sp.Definitions = make(map[string]spec.Schema, 150)
}
sp.Definitions[name] = *schema
}
// Clone deep-clones a schema
func Clone(schema *spec.Schema) *spec.Schema {
var sch spec.Schema
_ = swag.FromDynamicJSON(schema, &sch)
return &sch
}

View file

@ -1,201 +0,0 @@
package sortref
import (
"net/http"
"path"
"strconv"
"strings"
"github.com/go-openapi/jsonpointer"
"github.com/go-openapi/spec"
)
const (
paths = "paths"
responses = "responses"
parameters = "parameters"
definitions = "definitions"
)
var (
ignoredKeys map[string]struct{}
validMethods map[string]struct{}
)
func init() {
ignoredKeys = map[string]struct{}{
"schema": {},
"properties": {},
"not": {},
"anyOf": {},
"oneOf": {},
}
validMethods = map[string]struct{}{
"GET": {},
"HEAD": {},
"OPTIONS": {},
"PATCH": {},
"POST": {},
"PUT": {},
"DELETE": {},
}
}
// Key represent a key item constructed from /-separated segments
type Key struct {
Segments int
Key string
}
// Keys is a sortable collable collection of Keys
type Keys []Key
func (k Keys) Len() int { return len(k) }
func (k Keys) Swap(i, j int) { k[i], k[j] = k[j], k[i] }
func (k Keys) Less(i, j int) bool {
return k[i].Segments > k[j].Segments || (k[i].Segments == k[j].Segments && k[i].Key < k[j].Key)
}
// KeyParts construct a SplitKey with all its /-separated segments decomposed. It is sortable.
func KeyParts(key string) SplitKey {
var res []string
for _, part := range strings.Split(key[1:], "/") {
if part != "" {
res = append(res, jsonpointer.Unescape(part))
}
}
return res
}
// SplitKey holds of the parts of a /-separated key, so that their location may be determined.
type SplitKey []string
// IsDefinition is true when the split key is in the #/definitions section of a spec
func (s SplitKey) IsDefinition() bool {
return len(s) > 1 && s[0] == definitions
}
// DefinitionName yields the name of the definition
func (s SplitKey) DefinitionName() string {
if !s.IsDefinition() {
return ""
}
return s[1]
}
func (s SplitKey) isKeyName(i int) bool {
if i <= 0 {
return false
}
count := 0
for idx := i - 1; idx > 0; idx-- {
if s[idx] != "properties" {
break
}
count++
}
return count%2 != 0
}
// PartAdder know how to construct the components of a new name
type PartAdder func(string) []string
// BuildName builds a name from segments
func (s SplitKey) BuildName(segments []string, startIndex int, adder PartAdder) string {
for i, part := range s[startIndex:] {
if _, ignored := ignoredKeys[part]; !ignored || s.isKeyName(startIndex+i) {
segments = append(segments, adder(part)...)
}
}
return strings.Join(segments, " ")
}
// IsOperation is true when the split key is in the operations section
func (s SplitKey) IsOperation() bool {
return len(s) > 1 && s[0] == paths
}
// IsSharedOperationParam is true when the split key is in the parameters section of a path
func (s SplitKey) IsSharedOperationParam() bool {
return len(s) > 2 && s[0] == paths && s[2] == parameters
}
// IsSharedParam is true when the split key is in the #/parameters section of a spec
func (s SplitKey) IsSharedParam() bool {
return len(s) > 1 && s[0] == parameters
}
// IsOperationParam is true when the split key is in the parameters section of an operation
func (s SplitKey) IsOperationParam() bool {
return len(s) > 3 && s[0] == paths && s[3] == parameters
}
// IsOperationResponse is true when the split key is in the responses section of an operation
func (s SplitKey) IsOperationResponse() bool {
return len(s) > 3 && s[0] == paths && s[3] == responses
}
// IsSharedResponse is true when the split key is in the #/responses section of a spec
func (s SplitKey) IsSharedResponse() bool {
return len(s) > 1 && s[0] == responses
}
// IsDefaultResponse is true when the split key is the default response for an operation
func (s SplitKey) IsDefaultResponse() bool {
return len(s) > 4 && s[0] == paths && s[3] == responses && s[4] == "default"
}
// IsStatusCodeResponse is true when the split key is an operation response with a status code
func (s SplitKey) IsStatusCodeResponse() bool {
isInt := func() bool {
_, err := strconv.Atoi(s[4])
return err == nil
}
return len(s) > 4 && s[0] == paths && s[3] == responses && isInt()
}
// ResponseName yields either the status code or "Default" for a response
func (s SplitKey) ResponseName() string {
if s.IsStatusCodeResponse() {
code, _ := strconv.Atoi(s[4])
return http.StatusText(code)
}
if s.IsDefaultResponse() {
return "Default"
}
return ""
}
// PathItemRef constructs a $ref object from a split key of the form /{path}/{method}
func (s SplitKey) PathItemRef() spec.Ref {
if len(s) < 3 {
return spec.Ref{}
}
pth, method := s[1], s[2]
if _, isValidMethod := validMethods[strings.ToUpper(method)]; !isValidMethod && !strings.HasPrefix(method, "x-") {
return spec.Ref{}
}
return spec.MustCreateRef("#" + path.Join("/", paths, jsonpointer.Escape(pth), strings.ToUpper(method)))
}
// PathRef constructs a $ref object from a split key of the form /paths/{reference}
func (s SplitKey) PathRef() spec.Ref {
if !s.IsOperation() {
return spec.Ref{}
}
return spec.MustCreateRef("#" + path.Join("/", paths, jsonpointer.Escape(s[1])))
}

View file

@ -1,141 +0,0 @@
package sortref
import (
"reflect"
"sort"
"strings"
"github.com/go-openapi/analysis/internal/flatten/normalize"
"github.com/go-openapi/spec"
)
var depthGroupOrder = []string{
"sharedParam", "sharedResponse", "sharedOpParam", "opParam", "codeResponse", "defaultResponse", "definition",
}
type mapIterator struct {
len int
mapIter *reflect.MapIter
}
func (i *mapIterator) Next() bool {
return i.mapIter.Next()
}
func (i *mapIterator) Len() int {
return i.len
}
func (i *mapIterator) Key() string {
return i.mapIter.Key().String()
}
func mustMapIterator(anyMap interface{}) *mapIterator {
val := reflect.ValueOf(anyMap)
return &mapIterator{mapIter: val.MapRange(), len: val.Len()}
}
// DepthFirst sorts a map of anything. It groups keys by category
// (shared params, op param, statuscode response, default response, definitions)
// sort groups internally by number of parts in the key and lexical names
// flatten groups into a single list of keys
func DepthFirst(in interface{}) []string {
iterator := mustMapIterator(in)
sorted := make([]string, 0, iterator.Len())
grouped := make(map[string]Keys, iterator.Len())
for iterator.Next() {
k := iterator.Key()
split := KeyParts(k)
var pk string
if split.IsSharedOperationParam() {
pk = "sharedOpParam"
}
if split.IsOperationParam() {
pk = "opParam"
}
if split.IsStatusCodeResponse() {
pk = "codeResponse"
}
if split.IsDefaultResponse() {
pk = "defaultResponse"
}
if split.IsDefinition() {
pk = "definition"
}
if split.IsSharedParam() {
pk = "sharedParam"
}
if split.IsSharedResponse() {
pk = "sharedResponse"
}
grouped[pk] = append(grouped[pk], Key{Segments: len(split), Key: k})
}
for _, pk := range depthGroupOrder {
res := grouped[pk]
sort.Sort(res)
for _, v := range res {
sorted = append(sorted, v.Key)
}
}
return sorted
}
// topMostRefs is able to sort refs by hierarchical then lexicographic order,
// yielding refs ordered breadth-first.
type topmostRefs []string
func (k topmostRefs) Len() int { return len(k) }
func (k topmostRefs) Swap(i, j int) { k[i], k[j] = k[j], k[i] }
func (k topmostRefs) Less(i, j int) bool {
li, lj := len(strings.Split(k[i], "/")), len(strings.Split(k[j], "/"))
if li == lj {
return k[i] < k[j]
}
return li < lj
}
// TopmostFirst sorts references by depth
func TopmostFirst(refs []string) []string {
res := topmostRefs(refs)
sort.Sort(res)
return res
}
// RefRevIdx is a reverse index for references
type RefRevIdx struct {
Ref spec.Ref
Keys []string
}
// ReverseIndex builds a reverse index for references in schemas
func ReverseIndex(schemas map[string]spec.Ref, basePath string) map[string]RefRevIdx {
collected := make(map[string]RefRevIdx)
for key, schRef := range schemas {
// normalize paths before sorting,
// so we get together keys that are from the same external file
normalizedPath := normalize.Path(schRef, basePath)
entry, ok := collected[normalizedPath]
if ok {
entry.Keys = append(entry.Keys, key)
collected[normalizedPath] = entry
continue
}
collected[normalizedPath] = RefRevIdx{
Ref: schRef,
Keys: []string{key},
}
}
return collected
}

View file

@ -1,515 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package analysis
import (
"fmt"
"reflect"
"github.com/go-openapi/spec"
)
// Mixin modifies the primary swagger spec by adding the paths and
// definitions from the mixin specs. Top level parameters and
// responses from the mixins are also carried over. Operation id
// collisions are avoided by appending "Mixin<N>" but only if
// needed.
//
// The following parts of primary are subject to merge, filling empty details
// - Info
// - BasePath
// - Host
// - ExternalDocs
//
// Consider calling FixEmptyResponseDescriptions() on the modified primary
// if you read them from storage and they are valid to start with.
//
// Entries in "paths", "definitions", "parameters" and "responses" are
// added to the primary in the order of the given mixins. If the entry
// already exists in primary it is skipped with a warning message.
//
// The count of skipped entries (from collisions) is returned so any
// deviation from the number expected can flag a warning in your build
// scripts. Carefully review the collisions before accepting them;
// consider renaming things if possible.
//
// No key normalization takes place (paths, type defs,
// etc). Ensure they are canonical if your downstream tools do
// key normalization of any form.
//
// Merging schemes (http, https), and consumers/producers do not account for
// collisions.
func Mixin(primary *spec.Swagger, mixins ...*spec.Swagger) []string {
skipped := make([]string, 0, len(mixins))
opIDs := getOpIDs(primary)
initPrimary(primary)
for i, m := range mixins {
skipped = append(skipped, mergeSwaggerProps(primary, m)...)
skipped = append(skipped, mergeConsumes(primary, m)...)
skipped = append(skipped, mergeProduces(primary, m)...)
skipped = append(skipped, mergeTags(primary, m)...)
skipped = append(skipped, mergeSchemes(primary, m)...)
skipped = append(skipped, mergeSecurityDefinitions(primary, m)...)
skipped = append(skipped, mergeSecurityRequirements(primary, m)...)
skipped = append(skipped, mergeDefinitions(primary, m)...)
// merging paths requires a map of operationIDs to work with
skipped = append(skipped, mergePaths(primary, m, opIDs, i)...)
skipped = append(skipped, mergeParameters(primary, m)...)
skipped = append(skipped, mergeResponses(primary, m)...)
}
return skipped
}
// getOpIDs extracts all the paths.<path>.operationIds from the given
// spec and returns them as the keys in a map with 'true' values.
func getOpIDs(s *spec.Swagger) map[string]bool {
rv := make(map[string]bool)
if s.Paths == nil {
return rv
}
for _, v := range s.Paths.Paths {
piops := pathItemOps(v)
for _, op := range piops {
rv[op.ID] = true
}
}
return rv
}
func pathItemOps(p spec.PathItem) []*spec.Operation {
var rv []*spec.Operation
rv = appendOp(rv, p.Get)
rv = appendOp(rv, p.Put)
rv = appendOp(rv, p.Post)
rv = appendOp(rv, p.Delete)
rv = appendOp(rv, p.Head)
rv = appendOp(rv, p.Patch)
return rv
}
func appendOp(ops []*spec.Operation, op *spec.Operation) []*spec.Operation {
if op == nil {
return ops
}
return append(ops, op)
}
func mergeSecurityDefinitions(primary *spec.Swagger, m *spec.Swagger) (skipped []string) {
for k, v := range m.SecurityDefinitions {
if _, exists := primary.SecurityDefinitions[k]; exists {
warn := fmt.Sprintf(
"SecurityDefinitions entry '%v' already exists in primary or higher priority mixin, skipping\n", k)
skipped = append(skipped, warn)
continue
}
primary.SecurityDefinitions[k] = v
}
return
}
func mergeSecurityRequirements(primary *spec.Swagger, m *spec.Swagger) (skipped []string) {
for _, v := range m.Security {
found := false
for _, vv := range primary.Security {
if reflect.DeepEqual(v, vv) {
found = true
break
}
}
if found {
warn := fmt.Sprintf(
"Security requirement: '%v' already exists in primary or higher priority mixin, skipping\n", v)
skipped = append(skipped, warn)
continue
}
primary.Security = append(primary.Security, v)
}
return
}
func mergeDefinitions(primary *spec.Swagger, m *spec.Swagger) (skipped []string) {
for k, v := range m.Definitions {
// assume name collisions represent IDENTICAL type. careful.
if _, exists := primary.Definitions[k]; exists {
warn := fmt.Sprintf(
"definitions entry '%v' already exists in primary or higher priority mixin, skipping\n", k)
skipped = append(skipped, warn)
continue
}
primary.Definitions[k] = v
}
return
}
func mergePaths(primary *spec.Swagger, m *spec.Swagger, opIDs map[string]bool, mixIndex int) (skipped []string) {
if m.Paths != nil {
for k, v := range m.Paths.Paths {
if _, exists := primary.Paths.Paths[k]; exists {
warn := fmt.Sprintf(
"paths entry '%v' already exists in primary or higher priority mixin, skipping\n", k)
skipped = append(skipped, warn)
continue
}
// Swagger requires that operationIds be
// unique within a spec. If we find a
// collision we append "Mixin0" to the
// operatoinId we are adding, where 0 is mixin
// index. We assume that operationIds with
// all the proivded specs are already unique.
piops := pathItemOps(v)
for _, piop := range piops {
if opIDs[piop.ID] {
piop.ID = fmt.Sprintf("%v%v%v", piop.ID, "Mixin", mixIndex)
}
opIDs[piop.ID] = true
}
primary.Paths.Paths[k] = v
}
}
return
}
func mergeParameters(primary *spec.Swagger, m *spec.Swagger) (skipped []string) {
for k, v := range m.Parameters {
// could try to rename on conflict but would
// have to fix $refs in the mixin. Complain
// for now
if _, exists := primary.Parameters[k]; exists {
warn := fmt.Sprintf(
"top level parameters entry '%v' already exists in primary or higher priority mixin, skipping\n", k)
skipped = append(skipped, warn)
continue
}
primary.Parameters[k] = v
}
return
}
func mergeResponses(primary *spec.Swagger, m *spec.Swagger) (skipped []string) {
for k, v := range m.Responses {
// could try to rename on conflict but would
// have to fix $refs in the mixin. Complain
// for now
if _, exists := primary.Responses[k]; exists {
warn := fmt.Sprintf(
"top level responses entry '%v' already exists in primary or higher priority mixin, skipping\n", k)
skipped = append(skipped, warn)
continue
}
primary.Responses[k] = v
}
return skipped
}
func mergeConsumes(primary *spec.Swagger, m *spec.Swagger) []string {
for _, v := range m.Consumes {
found := false
for _, vv := range primary.Consumes {
if v == vv {
found = true
break
}
}
if found {
// no warning here: we just skip it
continue
}
primary.Consumes = append(primary.Consumes, v)
}
return []string{}
}
func mergeProduces(primary *spec.Swagger, m *spec.Swagger) []string {
for _, v := range m.Produces {
found := false
for _, vv := range primary.Produces {
if v == vv {
found = true
break
}
}
if found {
// no warning here: we just skip it
continue
}
primary.Produces = append(primary.Produces, v)
}
return []string{}
}
func mergeTags(primary *spec.Swagger, m *spec.Swagger) (skipped []string) {
for _, v := range m.Tags {
found := false
for _, vv := range primary.Tags {
if v.Name == vv.Name {
found = true
break
}
}
if found {
warn := fmt.Sprintf(
"top level tags entry with name '%v' already exists in primary or higher priority mixin, skipping\n",
v.Name,
)
skipped = append(skipped, warn)
continue
}
primary.Tags = append(primary.Tags, v)
}
return
}
func mergeSchemes(primary *spec.Swagger, m *spec.Swagger) []string {
for _, v := range m.Schemes {
found := false
for _, vv := range primary.Schemes {
if v == vv {
found = true
break
}
}
if found {
// no warning here: we just skip it
continue
}
primary.Schemes = append(primary.Schemes, v)
}
return []string{}
}
func mergeSwaggerProps(primary *spec.Swagger, m *spec.Swagger) []string {
var skipped, skippedInfo, skippedDocs []string
primary.Extensions, skipped = mergeExtensions(primary.Extensions, m.Extensions)
// merging details in swagger top properties
if primary.Host == "" {
primary.Host = m.Host
}
if primary.BasePath == "" {
primary.BasePath = m.BasePath
}
if primary.Info == nil {
primary.Info = m.Info
} else if m.Info != nil {
skippedInfo = mergeInfo(primary.Info, m.Info)
skipped = append(skipped, skippedInfo...)
}
if primary.ExternalDocs == nil {
primary.ExternalDocs = m.ExternalDocs
} else if m != nil {
skippedDocs = mergeExternalDocs(primary.ExternalDocs, m.ExternalDocs)
skipped = append(skipped, skippedDocs...)
}
return skipped
}
//nolint:unparam
func mergeExternalDocs(primary *spec.ExternalDocumentation, m *spec.ExternalDocumentation) []string {
if primary.Description == "" {
primary.Description = m.Description
}
if primary.URL == "" {
primary.URL = m.URL
}
return nil
}
func mergeInfo(primary *spec.Info, m *spec.Info) []string {
var sk, skipped []string
primary.Extensions, sk = mergeExtensions(primary.Extensions, m.Extensions)
skipped = append(skipped, sk...)
if primary.Description == "" {
primary.Description = m.Description
}
if primary.Title == "" {
primary.Description = m.Description
}
if primary.TermsOfService == "" {
primary.TermsOfService = m.TermsOfService
}
if primary.Version == "" {
primary.Version = m.Version
}
if primary.Contact == nil {
primary.Contact = m.Contact
} else if m.Contact != nil {
var csk []string
primary.Contact.Extensions, csk = mergeExtensions(primary.Contact.Extensions, m.Contact.Extensions)
skipped = append(skipped, csk...)
if primary.Contact.Name == "" {
primary.Contact.Name = m.Contact.Name
}
if primary.Contact.URL == "" {
primary.Contact.URL = m.Contact.URL
}
if primary.Contact.Email == "" {
primary.Contact.Email = m.Contact.Email
}
}
if primary.License == nil {
primary.License = m.License
} else if m.License != nil {
var lsk []string
primary.License.Extensions, lsk = mergeExtensions(primary.License.Extensions, m.License.Extensions)
skipped = append(skipped, lsk...)
if primary.License.Name == "" {
primary.License.Name = m.License.Name
}
if primary.License.URL == "" {
primary.License.URL = m.License.URL
}
}
return skipped
}
func mergeExtensions(primary spec.Extensions, m spec.Extensions) (result spec.Extensions, skipped []string) {
if primary == nil {
result = m
return
}
if m == nil {
result = primary
return
}
result = primary
for k, v := range m {
if _, found := primary[k]; found {
skipped = append(skipped, k)
continue
}
primary[k] = v
}
return
}
func initPrimary(primary *spec.Swagger) {
if primary.SecurityDefinitions == nil {
primary.SecurityDefinitions = make(map[string]*spec.SecurityScheme)
}
if primary.Security == nil {
primary.Security = make([]map[string][]string, 0, 10)
}
if primary.Produces == nil {
primary.Produces = make([]string, 0, 10)
}
if primary.Consumes == nil {
primary.Consumes = make([]string, 0, 10)
}
if primary.Tags == nil {
primary.Tags = make([]spec.Tag, 0, 10)
}
if primary.Schemes == nil {
primary.Schemes = make([]string, 0, 10)
}
if primary.Paths == nil {
primary.Paths = &spec.Paths{Paths: make(map[string]spec.PathItem)}
}
if primary.Paths.Paths == nil {
primary.Paths.Paths = make(map[string]spec.PathItem)
}
if primary.Definitions == nil {
primary.Definitions = make(spec.Definitions)
}
if primary.Parameters == nil {
primary.Parameters = make(map[string]spec.Parameter)
}
if primary.Responses == nil {
primary.Responses = make(map[string]spec.Response)
}
}

View file

@ -1,256 +0,0 @@
package analysis
import (
"errors"
"github.com/go-openapi/spec"
"github.com/go-openapi/strfmt"
)
// SchemaOpts configures the schema analyzer
type SchemaOpts struct {
Schema *spec.Schema
Root interface{}
BasePath string
_ struct{}
}
// Schema analysis, will classify the schema according to known
// patterns.
func Schema(opts SchemaOpts) (*AnalyzedSchema, error) {
if opts.Schema == nil {
return nil, errors.New("no schema to analyze")
}
a := &AnalyzedSchema{
schema: opts.Schema,
root: opts.Root,
basePath: opts.BasePath,
}
a.initializeFlags()
a.inferKnownType()
a.inferEnum()
a.inferBaseType()
if err := a.inferMap(); err != nil {
return nil, err
}
if err := a.inferArray(); err != nil {
return nil, err
}
a.inferTuple()
if err := a.inferFromRef(); err != nil {
return nil, err
}
a.inferSimpleSchema()
return a, nil
}
// AnalyzedSchema indicates what the schema represents
type AnalyzedSchema struct {
schema *spec.Schema
root interface{}
basePath string
hasProps bool
hasAllOf bool
hasItems bool
hasAdditionalProps bool
hasAdditionalItems bool
hasRef bool
IsKnownType bool
IsSimpleSchema bool
IsArray bool
IsSimpleArray bool
IsMap bool
IsSimpleMap bool
IsExtendedObject bool
IsTuple bool
IsTupleWithExtra bool
IsBaseType bool
IsEnum bool
}
// Inherits copies value fields from other onto this schema
func (a *AnalyzedSchema) inherits(other *AnalyzedSchema) {
if other == nil {
return
}
a.hasProps = other.hasProps
a.hasAllOf = other.hasAllOf
a.hasItems = other.hasItems
a.hasAdditionalItems = other.hasAdditionalItems
a.hasAdditionalProps = other.hasAdditionalProps
a.hasRef = other.hasRef
a.IsKnownType = other.IsKnownType
a.IsSimpleSchema = other.IsSimpleSchema
a.IsArray = other.IsArray
a.IsSimpleArray = other.IsSimpleArray
a.IsMap = other.IsMap
a.IsSimpleMap = other.IsSimpleMap
a.IsExtendedObject = other.IsExtendedObject
a.IsTuple = other.IsTuple
a.IsTupleWithExtra = other.IsTupleWithExtra
a.IsBaseType = other.IsBaseType
a.IsEnum = other.IsEnum
}
func (a *AnalyzedSchema) inferFromRef() error {
if a.hasRef {
sch := new(spec.Schema)
sch.Ref = a.schema.Ref
err := spec.ExpandSchema(sch, a.root, nil)
if err != nil {
return err
}
rsch, err := Schema(SchemaOpts{
Schema: sch,
Root: a.root,
BasePath: a.basePath,
})
if err != nil {
// NOTE(fredbi): currently the only cause for errors is
// unresolved ref. Since spec.ExpandSchema() expands the
// schema recursively, there is no chance to get there,
// until we add more causes for error in this schema analysis.
return err
}
a.inherits(rsch)
}
return nil
}
func (a *AnalyzedSchema) inferSimpleSchema() {
a.IsSimpleSchema = a.IsKnownType || a.IsSimpleArray || a.IsSimpleMap
}
func (a *AnalyzedSchema) inferKnownType() {
tpe := a.schema.Type
format := a.schema.Format
a.IsKnownType = tpe.Contains("boolean") ||
tpe.Contains("integer") ||
tpe.Contains("number") ||
tpe.Contains("string") ||
(format != "" && strfmt.Default.ContainsName(format)) ||
(a.isObjectType() && !a.hasProps && !a.hasAllOf && !a.hasAdditionalProps && !a.hasAdditionalItems)
}
func (a *AnalyzedSchema) inferMap() error {
if !a.isObjectType() {
return nil
}
hasExtra := a.hasProps || a.hasAllOf
a.IsMap = a.hasAdditionalProps && !hasExtra
a.IsExtendedObject = a.hasAdditionalProps && hasExtra
if !a.IsMap {
return nil
}
// maps
if a.schema.AdditionalProperties.Schema != nil {
msch, err := Schema(SchemaOpts{
Schema: a.schema.AdditionalProperties.Schema,
Root: a.root,
BasePath: a.basePath,
})
if err != nil {
return err
}
a.IsSimpleMap = msch.IsSimpleSchema
} else if a.schema.AdditionalProperties.Allows {
a.IsSimpleMap = true
}
return nil
}
func (a *AnalyzedSchema) inferArray() error {
// an array has Items defined as an object schema, otherwise we qualify this JSON array as a tuple
// (yes, even if the Items array contains only one element).
// arrays in JSON schema may be unrestricted (i.e no Items specified).
// Note that arrays in Swagger MUST have Items. Nonetheless, we analyze unrestricted arrays.
//
// NOTE: the spec package misses the distinction between:
// items: [] and items: {}, so we consider both arrays here.
a.IsArray = a.isArrayType() && (a.schema.Items == nil || a.schema.Items.Schemas == nil)
if a.IsArray && a.hasItems {
if a.schema.Items.Schema != nil {
itsch, err := Schema(SchemaOpts{
Schema: a.schema.Items.Schema,
Root: a.root,
BasePath: a.basePath,
})
if err != nil {
return err
}
a.IsSimpleArray = itsch.IsSimpleSchema
}
}
if a.IsArray && !a.hasItems {
a.IsSimpleArray = true
}
return nil
}
func (a *AnalyzedSchema) inferTuple() {
tuple := a.hasItems && a.schema.Items.Schemas != nil
a.IsTuple = tuple && !a.hasAdditionalItems
a.IsTupleWithExtra = tuple && a.hasAdditionalItems
}
func (a *AnalyzedSchema) inferBaseType() {
if a.isObjectType() {
a.IsBaseType = a.schema.Discriminator != ""
}
}
func (a *AnalyzedSchema) inferEnum() {
a.IsEnum = len(a.schema.Enum) > 0
}
func (a *AnalyzedSchema) initializeFlags() {
a.hasProps = len(a.schema.Properties) > 0
a.hasAllOf = len(a.schema.AllOf) > 0
a.hasRef = a.schema.Ref.String() != ""
a.hasItems = a.schema.Items != nil &&
(a.schema.Items.Schema != nil || len(a.schema.Items.Schemas) > 0)
a.hasAdditionalProps = a.schema.AdditionalProperties != nil &&
(a.schema.AdditionalProperties.Schema != nil || a.schema.AdditionalProperties.Allows)
a.hasAdditionalItems = a.schema.AdditionalItems != nil &&
(a.schema.AdditionalItems.Schema != nil || a.schema.AdditionalItems.Allows)
}
func (a *AnalyzedSchema) isObjectType() bool {
return !a.hasRef && (a.schema.Type == nil || a.schema.Type.Contains("") || a.schema.Type.Contains("object"))
}
func (a *AnalyzedSchema) isArrayType() bool {
return !a.hasRef && (a.schema.Type != nil && a.schema.Type.Contains("array"))
}
// isAnalyzedAsComplex determines if an analyzed schema is eligible to flattening (i.e. it is "complex").
//
// Complex means the schema is any of:
// - a simple type (primitive)
// - an array of something (items are possibly complex ; if this is the case, items will generate a definition)
// - a map of something (additionalProperties are possibly complex ; if this is the case, additionalProperties will
// generate a definition)
func (a *AnalyzedSchema) isAnalyzedAsComplex() bool {
return !a.IsSimpleSchema && !a.IsArray && !a.IsMap
}

View file

@ -1 +0,0 @@
*.go text eol=lf

View file

@ -1,2 +0,0 @@
secrets.yml
coverage.out

View file

@ -1,55 +0,0 @@
linters-settings:
gocyclo:
min-complexity: 45
dupl:
threshold: 200
goconst:
min-len: 2
min-occurrences: 3
linters:
enable-all: true
disable:
- unparam
- lll
- gochecknoinits
- gochecknoglobals
- funlen
- godox
- gocognit
- whitespace
- wsl
- wrapcheck
- testpackage
- nlreturn
- errorlint
- nestif
- godot
- gofumpt
- paralleltest
- tparallel
- thelper
- exhaustruct
- varnamelen
- gci
- depguard
- errchkjson
- inamedparam
- nonamedreturns
- musttag
- ireturn
- forcetypeassert
- cyclop
# deprecated linters
#- deadcode
#- interfacer
#- scopelint
#- varcheck
#- structcheck
#- golint
#- nosnakecase
#- maligned
#- goerr113
#- ifshort
#- gomnd
#- exhaustivestruct

View file

@ -1,74 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
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
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
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
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.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
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]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View file

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1,8 +0,0 @@
# OpenAPI errors [![Build Status](https://github.com/go-openapi/errors/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/errors/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/errors/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/errors)
[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/errors/master/LICENSE)
[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/errors.svg)](https://pkg.go.dev/github.com/go-openapi/errors)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/errors)](https://goreportcard.com/report/github.com/go-openapi/errors)
Shared errors and error interface used throughout the various libraries found in the go-openapi toolkit.

View file

@ -1,192 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package errors
import (
"encoding/json"
"fmt"
"net/http"
"reflect"
"strings"
)
// DefaultHTTPCode is used when the error Code cannot be used as an HTTP code.
var DefaultHTTPCode = http.StatusUnprocessableEntity
// Error represents a error interface all swagger framework errors implement
type Error interface {
error
Code() int32
}
type apiError struct {
code int32
message string
}
func (a *apiError) Error() string {
return a.message
}
func (a *apiError) Code() int32 {
return a.code
}
// MarshalJSON implements the JSON encoding interface
func (a apiError) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}{
"code": a.code,
"message": a.message,
})
}
// New creates a new API error with a code and a message
func New(code int32, message string, args ...interface{}) Error {
if len(args) > 0 {
return &apiError{
code: code,
message: fmt.Sprintf(message, args...),
}
}
return &apiError{
code: code,
message: message,
}
}
// NotFound creates a new not found error
func NotFound(message string, args ...interface{}) Error {
if message == "" {
message = "Not found"
}
return New(http.StatusNotFound, fmt.Sprintf(message, args...))
}
// NotImplemented creates a new not implemented error
func NotImplemented(message string) Error {
return New(http.StatusNotImplemented, message)
}
// MethodNotAllowedError represents an error for when the path matches but the method doesn't
type MethodNotAllowedError struct {
code int32
Allowed []string
message string
}
func (m *MethodNotAllowedError) Error() string {
return m.message
}
// Code the error code
func (m *MethodNotAllowedError) Code() int32 {
return m.code
}
// MarshalJSON implements the JSON encoding interface
func (m MethodNotAllowedError) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}{
"code": m.code,
"message": m.message,
"allowed": m.Allowed,
})
}
func errorAsJSON(err Error) []byte {
//nolint:errchkjson
b, _ := json.Marshal(struct {
Code int32 `json:"code"`
Message string `json:"message"`
}{err.Code(), err.Error()})
return b
}
func flattenComposite(errs *CompositeError) *CompositeError {
var res []error
for _, er := range errs.Errors {
switch e := er.(type) {
case *CompositeError:
if e != nil && len(e.Errors) > 0 {
flat := flattenComposite(e)
if len(flat.Errors) > 0 {
res = append(res, flat.Errors...)
}
}
default:
if e != nil {
res = append(res, e)
}
}
}
return CompositeValidationError(res...)
}
// MethodNotAllowed creates a new method not allowed error
func MethodNotAllowed(requested string, allow []string) Error {
msg := fmt.Sprintf("method %s is not allowed, but [%s] are", requested, strings.Join(allow, ","))
return &MethodNotAllowedError{
code: http.StatusMethodNotAllowed,
Allowed: allow,
message: msg,
}
}
// ServeError implements the http error handler interface
func ServeError(rw http.ResponseWriter, r *http.Request, err error) {
rw.Header().Set("Content-Type", "application/json")
switch e := err.(type) {
case *CompositeError:
er := flattenComposite(e)
// strips composite errors to first element only
if len(er.Errors) > 0 {
ServeError(rw, r, er.Errors[0])
} else {
// guard against empty CompositeError (invalid construct)
ServeError(rw, r, nil)
}
case *MethodNotAllowedError:
rw.Header().Add("Allow", strings.Join(e.Allowed, ","))
rw.WriteHeader(asHTTPCode(int(e.Code())))
if r == nil || r.Method != http.MethodHead {
_, _ = rw.Write(errorAsJSON(e))
}
case Error:
value := reflect.ValueOf(e)
if value.Kind() == reflect.Ptr && value.IsNil() {
rw.WriteHeader(http.StatusInternalServerError)
_, _ = rw.Write(errorAsJSON(New(http.StatusInternalServerError, "Unknown error")))
return
}
rw.WriteHeader(asHTTPCode(int(e.Code())))
if r == nil || r.Method != http.MethodHead {
_, _ = rw.Write(errorAsJSON(e))
}
case nil:
rw.WriteHeader(http.StatusInternalServerError)
_, _ = rw.Write(errorAsJSON(New(http.StatusInternalServerError, "Unknown error")))
default:
rw.WriteHeader(http.StatusInternalServerError)
if r == nil || r.Method != http.MethodHead {
_, _ = rw.Write(errorAsJSON(New(http.StatusInternalServerError, err.Error())))
}
}
}
func asHTTPCode(input int) int {
if input >= maximumValidHTTPCode {
return DefaultHTTPCode
}
return input
}

View file

@ -1,22 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package errors
import "net/http"
// Unauthenticated returns an unauthenticated error
func Unauthenticated(scheme string) Error {
return New(http.StatusUnauthorized, "unauthenticated for %s", scheme)
}

View file

@ -1,26 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
Package errors provides an Error interface and several concrete types
implementing this interface to manage API errors and JSON-schema validation
errors.
A middleware handler ServeError() is provided to serve the errors types
it defines.
It is used throughout the various go-openapi toolkit libraries
(https://github.com/go-openapi).
*/
package errors

View file

@ -1,103 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package errors
import (
"encoding/json"
"fmt"
"net/http"
)
// Validation represents a failure of a precondition
type Validation struct { //nolint: errname
code int32
Name string
In string
Value interface{}
message string
Values []interface{}
}
func (e *Validation) Error() string {
return e.message
}
// Code the error code
func (e *Validation) Code() int32 {
return e.code
}
// MarshalJSON implements the JSON encoding interface
func (e Validation) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}{
"code": e.code,
"message": e.message,
"in": e.In,
"name": e.Name,
"value": e.Value,
"values": e.Values,
})
}
// ValidateName sets the name for a validation or updates it for a nested property
func (e *Validation) ValidateName(name string) *Validation {
if name != "" {
if e.Name == "" {
e.Name = name
e.message = name + e.message
} else {
e.Name = name + "." + e.Name
e.message = name + "." + e.message
}
}
return e
}
const (
contentTypeFail = `unsupported media type %q, only %v are allowed`
responseFormatFail = `unsupported media type requested, only %v are available`
)
// InvalidContentType error for an invalid content type
func InvalidContentType(value string, allowed []string) *Validation {
values := make([]interface{}, 0, len(allowed))
for _, v := range allowed {
values = append(values, v)
}
return &Validation{
code: http.StatusUnsupportedMediaType,
Name: "Content-Type",
In: "header",
Value: value,
Values: values,
message: fmt.Sprintf(contentTypeFail, value, allowed),
}
}
// InvalidResponseFormat error for an unacceptable response format request
func InvalidResponseFormat(value string, allowed []string) *Validation {
values := make([]interface{}, 0, len(allowed))
for _, v := range allowed {
values = append(values, v)
}
return &Validation{
code: http.StatusNotAcceptable,
Name: "Accept",
In: "header",
Value: value,
Values: values,
message: fmt.Sprintf(responseFormatFail, allowed),
}
}

View file

@ -1,50 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package errors
import (
"bytes"
"fmt"
"strings"
)
// APIVerificationFailed is an error that contains all the missing info for a mismatched section
// between the api registrations and the api spec
type APIVerificationFailed struct { //nolint: errname
Section string `json:"section,omitempty"`
MissingSpecification []string `json:"missingSpecification,omitempty"`
MissingRegistration []string `json:"missingRegistration,omitempty"`
}
func (v *APIVerificationFailed) Error() string {
buf := bytes.NewBuffer(nil)
hasRegMissing := len(v.MissingRegistration) > 0
hasSpecMissing := len(v.MissingSpecification) > 0
if hasRegMissing {
buf.WriteString(fmt.Sprintf("missing [%s] %s registrations", strings.Join(v.MissingRegistration, ", "), v.Section))
}
if hasRegMissing && hasSpecMissing {
buf.WriteString("\n")
}
if hasSpecMissing {
buf.WriteString(fmt.Sprintf("missing from spec file [%s] %s", strings.Join(v.MissingSpecification, ", "), v.Section))
}
return buf.String()
}

View file

@ -1,79 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package errors
import (
"encoding/json"
"fmt"
"net/http"
)
// ParseError represents a parsing error
type ParseError struct {
code int32
Name string
In string
Value string
Reason error
message string
}
func (e *ParseError) Error() string {
return e.message
}
// Code returns the http status code for this error
func (e *ParseError) Code() int32 {
return e.code
}
// MarshalJSON implements the JSON encoding interface
func (e ParseError) MarshalJSON() ([]byte, error) {
var reason string
if e.Reason != nil {
reason = e.Reason.Error()
}
return json.Marshal(map[string]interface{}{
"code": e.code,
"message": e.message,
"in": e.In,
"name": e.Name,
"value": e.Value,
"reason": reason,
})
}
const (
parseErrorTemplContent = `parsing %s %s from %q failed, because %s`
parseErrorTemplContentNoIn = `parsing %s from %q failed, because %s`
)
// NewParseError creates a new parse error
func NewParseError(name, in, value string, reason error) *ParseError {
var msg string
if in == "" {
msg = fmt.Sprintf(parseErrorTemplContentNoIn, name, value, reason)
} else {
msg = fmt.Sprintf(parseErrorTemplContent, name, in, value, reason)
}
return &ParseError{
code: http.StatusBadRequest,
Name: name,
In: in,
Value: value,
Reason: reason,
message: msg,
}
}

View file

@ -1,619 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package errors
import (
"encoding/json"
"fmt"
"net/http"
"strings"
)
const (
invalidType = "%s is an invalid type name"
typeFail = "%s in %s must be of type %s"
typeFailWithData = "%s in %s must be of type %s: %q"
typeFailWithError = "%s in %s must be of type %s, because: %s"
requiredFail = "%s in %s is required"
readOnlyFail = "%s in %s is readOnly"
tooLongMessage = "%s in %s should be at most %d chars long"
tooShortMessage = "%s in %s should be at least %d chars long"
patternFail = "%s in %s should match '%s'"
enumFail = "%s in %s should be one of %v"
multipleOfFail = "%s in %s should be a multiple of %v"
maximumIncFail = "%s in %s should be less than or equal to %v"
maximumExcFail = "%s in %s should be less than %v"
minIncFail = "%s in %s should be greater than or equal to %v"
minExcFail = "%s in %s should be greater than %v"
uniqueFail = "%s in %s shouldn't contain duplicates"
maximumItemsFail = "%s in %s should have at most %d items"
minItemsFail = "%s in %s should have at least %d items"
typeFailNoIn = "%s must be of type %s"
typeFailWithDataNoIn = "%s must be of type %s: %q"
typeFailWithErrorNoIn = "%s must be of type %s, because: %s"
requiredFailNoIn = "%s is required"
readOnlyFailNoIn = "%s is readOnly"
tooLongMessageNoIn = "%s should be at most %d chars long"
tooShortMessageNoIn = "%s should be at least %d chars long"
patternFailNoIn = "%s should match '%s'"
enumFailNoIn = "%s should be one of %v"
multipleOfFailNoIn = "%s should be a multiple of %v"
maximumIncFailNoIn = "%s should be less than or equal to %v"
maximumExcFailNoIn = "%s should be less than %v"
minIncFailNoIn = "%s should be greater than or equal to %v"
minExcFailNoIn = "%s should be greater than %v"
uniqueFailNoIn = "%s shouldn't contain duplicates"
maximumItemsFailNoIn = "%s should have at most %d items"
minItemsFailNoIn = "%s should have at least %d items"
noAdditionalItems = "%s in %s can't have additional items"
noAdditionalItemsNoIn = "%s can't have additional items"
tooFewProperties = "%s in %s should have at least %d properties"
tooFewPropertiesNoIn = "%s should have at least %d properties"
tooManyProperties = "%s in %s should have at most %d properties"
tooManyPropertiesNoIn = "%s should have at most %d properties"
unallowedProperty = "%s.%s in %s is a forbidden property"
unallowedPropertyNoIn = "%s.%s is a forbidden property"
failedAllPatternProps = "%s.%s in %s failed all pattern properties"
failedAllPatternPropsNoIn = "%s.%s failed all pattern properties"
multipleOfMustBePositive = "factor MultipleOf declared for %s must be positive: %v"
)
const maximumValidHTTPCode = 600
// All code responses can be used to differentiate errors for different handling
// by the consuming program
const (
// CompositeErrorCode remains 422 for backwards-compatibility
// and to separate it from validation errors with cause
CompositeErrorCode = http.StatusUnprocessableEntity
// InvalidTypeCode is used for any subclass of invalid types
InvalidTypeCode = maximumValidHTTPCode + iota
RequiredFailCode
TooLongFailCode
TooShortFailCode
PatternFailCode
EnumFailCode
MultipleOfFailCode
MaxFailCode
MinFailCode
UniqueFailCode
MaxItemsFailCode
MinItemsFailCode
NoAdditionalItemsCode
TooFewPropertiesCode
TooManyPropertiesCode
UnallowedPropertyCode
FailedAllPatternPropsCode
MultipleOfMustBePositiveCode
ReadOnlyFailCode
)
// CompositeError is an error that groups several errors together
type CompositeError struct {
Errors []error
code int32
message string
}
// Code for this error
func (c *CompositeError) Code() int32 {
return c.code
}
func (c *CompositeError) Error() string {
if len(c.Errors) > 0 {
msgs := []string{c.message + ":"}
for _, e := range c.Errors {
msgs = append(msgs, e.Error())
}
return strings.Join(msgs, "\n")
}
return c.message
}
func (c *CompositeError) Unwrap() []error {
return c.Errors
}
// MarshalJSON implements the JSON encoding interface
func (c CompositeError) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}{
"code": c.code,
"message": c.message,
"errors": c.Errors,
})
}
// CompositeValidationError an error to wrap a bunch of other errors
func CompositeValidationError(errors ...error) *CompositeError {
return &CompositeError{
code: CompositeErrorCode,
Errors: append(make([]error, 0, len(errors)), errors...),
message: "validation failure list",
}
}
// ValidateName recursively sets the name for all validations or updates them for nested properties
func (c *CompositeError) ValidateName(name string) *CompositeError {
for i, e := range c.Errors {
if ve, ok := e.(*Validation); ok {
c.Errors[i] = ve.ValidateName(name)
} else if ce, ok := e.(*CompositeError); ok {
c.Errors[i] = ce.ValidateName(name)
}
}
return c
}
// FailedAllPatternProperties an error for when the property doesn't match a pattern
func FailedAllPatternProperties(name, in, key string) *Validation {
msg := fmt.Sprintf(failedAllPatternProps, name, key, in)
if in == "" {
msg = fmt.Sprintf(failedAllPatternPropsNoIn, name, key)
}
return &Validation{
code: FailedAllPatternPropsCode,
Name: name,
In: in,
Value: key,
message: msg,
}
}
// PropertyNotAllowed an error for when the property doesn't match a pattern
func PropertyNotAllowed(name, in, key string) *Validation {
msg := fmt.Sprintf(unallowedProperty, name, key, in)
if in == "" {
msg = fmt.Sprintf(unallowedPropertyNoIn, name, key)
}
return &Validation{
code: UnallowedPropertyCode,
Name: name,
In: in,
Value: key,
message: msg,
}
}
// TooFewProperties an error for an object with too few properties
func TooFewProperties(name, in string, n int64) *Validation {
msg := fmt.Sprintf(tooFewProperties, name, in, n)
if in == "" {
msg = fmt.Sprintf(tooFewPropertiesNoIn, name, n)
}
return &Validation{
code: TooFewPropertiesCode,
Name: name,
In: in,
Value: n,
message: msg,
}
}
// TooManyProperties an error for an object with too many properties
func TooManyProperties(name, in string, n int64) *Validation {
msg := fmt.Sprintf(tooManyProperties, name, in, n)
if in == "" {
msg = fmt.Sprintf(tooManyPropertiesNoIn, name, n)
}
return &Validation{
code: TooManyPropertiesCode,
Name: name,
In: in,
Value: n,
message: msg,
}
}
// AdditionalItemsNotAllowed an error for invalid additional items
func AdditionalItemsNotAllowed(name, in string) *Validation {
msg := fmt.Sprintf(noAdditionalItems, name, in)
if in == "" {
msg = fmt.Sprintf(noAdditionalItemsNoIn, name)
}
return &Validation{
code: NoAdditionalItemsCode,
Name: name,
In: in,
message: msg,
}
}
// InvalidCollectionFormat another flavor of invalid type error
func InvalidCollectionFormat(name, in, format string) *Validation {
return &Validation{
code: InvalidTypeCode,
Name: name,
In: in,
Value: format,
message: fmt.Sprintf("the collection format %q is not supported for the %s param %q", format, in, name),
}
}
// InvalidTypeName an error for when the type is invalid
func InvalidTypeName(typeName string) *Validation {
return &Validation{
code: InvalidTypeCode,
Value: typeName,
message: fmt.Sprintf(invalidType, typeName),
}
}
// InvalidType creates an error for when the type is invalid
func InvalidType(name, in, typeName string, value interface{}) *Validation {
var message string
if in != "" {
switch value.(type) {
case string:
message = fmt.Sprintf(typeFailWithData, name, in, typeName, value)
case error:
message = fmt.Sprintf(typeFailWithError, name, in, typeName, value)
default:
message = fmt.Sprintf(typeFail, name, in, typeName)
}
} else {
switch value.(type) {
case string:
message = fmt.Sprintf(typeFailWithDataNoIn, name, typeName, value)
case error:
message = fmt.Sprintf(typeFailWithErrorNoIn, name, typeName, value)
default:
message = fmt.Sprintf(typeFailNoIn, name, typeName)
}
}
return &Validation{
code: InvalidTypeCode,
Name: name,
In: in,
Value: value,
message: message,
}
}
// DuplicateItems error for when an array contains duplicates
func DuplicateItems(name, in string) *Validation {
msg := fmt.Sprintf(uniqueFail, name, in)
if in == "" {
msg = fmt.Sprintf(uniqueFailNoIn, name)
}
return &Validation{
code: UniqueFailCode,
Name: name,
In: in,
message: msg,
}
}
// TooManyItems error for when an array contains too many items
func TooManyItems(name, in string, maximum int64, value interface{}) *Validation {
msg := fmt.Sprintf(maximumItemsFail, name, in, maximum)
if in == "" {
msg = fmt.Sprintf(maximumItemsFailNoIn, name, maximum)
}
return &Validation{
code: MaxItemsFailCode,
Name: name,
In: in,
Value: value,
message: msg,
}
}
// TooFewItems error for when an array contains too few items
func TooFewItems(name, in string, minimum int64, value interface{}) *Validation {
msg := fmt.Sprintf(minItemsFail, name, in, minimum)
if in == "" {
msg = fmt.Sprintf(minItemsFailNoIn, name, minimum)
}
return &Validation{
code: MinItemsFailCode,
Name: name,
In: in,
Value: value,
message: msg,
}
}
// ExceedsMaximumInt error for when maximumimum validation fails
func ExceedsMaximumInt(name, in string, maximum int64, exclusive bool, value interface{}) *Validation {
var message string
if in == "" {
m := maximumIncFailNoIn
if exclusive {
m = maximumExcFailNoIn
}
message = fmt.Sprintf(m, name, maximum)
} else {
m := maximumIncFail
if exclusive {
m = maximumExcFail
}
message = fmt.Sprintf(m, name, in, maximum)
}
return &Validation{
code: MaxFailCode,
Name: name,
In: in,
Value: value,
message: message,
}
}
// ExceedsMaximumUint error for when maximumimum validation fails
func ExceedsMaximumUint(name, in string, maximum uint64, exclusive bool, value interface{}) *Validation {
var message string
if in == "" {
m := maximumIncFailNoIn
if exclusive {
m = maximumExcFailNoIn
}
message = fmt.Sprintf(m, name, maximum)
} else {
m := maximumIncFail
if exclusive {
m = maximumExcFail
}
message = fmt.Sprintf(m, name, in, maximum)
}
return &Validation{
code: MaxFailCode,
Name: name,
In: in,
Value: value,
message: message,
}
}
// ExceedsMaximum error for when maximumimum validation fails
func ExceedsMaximum(name, in string, maximum float64, exclusive bool, value interface{}) *Validation {
var message string
if in == "" {
m := maximumIncFailNoIn
if exclusive {
m = maximumExcFailNoIn
}
message = fmt.Sprintf(m, name, maximum)
} else {
m := maximumIncFail
if exclusive {
m = maximumExcFail
}
message = fmt.Sprintf(m, name, in, maximum)
}
return &Validation{
code: MaxFailCode,
Name: name,
In: in,
Value: value,
message: message,
}
}
// ExceedsMinimumInt error for when minimum validation fails
func ExceedsMinimumInt(name, in string, minimum int64, exclusive bool, value interface{}) *Validation {
var message string
if in == "" {
m := minIncFailNoIn
if exclusive {
m = minExcFailNoIn
}
message = fmt.Sprintf(m, name, minimum)
} else {
m := minIncFail
if exclusive {
m = minExcFail
}
message = fmt.Sprintf(m, name, in, minimum)
}
return &Validation{
code: MinFailCode,
Name: name,
In: in,
Value: value,
message: message,
}
}
// ExceedsMinimumUint error for when minimum validation fails
func ExceedsMinimumUint(name, in string, minimum uint64, exclusive bool, value interface{}) *Validation {
var message string
if in == "" {
m := minIncFailNoIn
if exclusive {
m = minExcFailNoIn
}
message = fmt.Sprintf(m, name, minimum)
} else {
m := minIncFail
if exclusive {
m = minExcFail
}
message = fmt.Sprintf(m, name, in, minimum)
}
return &Validation{
code: MinFailCode,
Name: name,
In: in,
Value: value,
message: message,
}
}
// ExceedsMinimum error for when minimum validation fails
func ExceedsMinimum(name, in string, minimum float64, exclusive bool, value interface{}) *Validation {
var message string
if in == "" {
m := minIncFailNoIn
if exclusive {
m = minExcFailNoIn
}
message = fmt.Sprintf(m, name, minimum)
} else {
m := minIncFail
if exclusive {
m = minExcFail
}
message = fmt.Sprintf(m, name, in, minimum)
}
return &Validation{
code: MinFailCode,
Name: name,
In: in,
Value: value,
message: message,
}
}
// NotMultipleOf error for when multiple of validation fails
func NotMultipleOf(name, in string, multiple, value interface{}) *Validation {
var msg string
if in == "" {
msg = fmt.Sprintf(multipleOfFailNoIn, name, multiple)
} else {
msg = fmt.Sprintf(multipleOfFail, name, in, multiple)
}
return &Validation{
code: MultipleOfFailCode,
Name: name,
In: in,
Value: value,
message: msg,
}
}
// EnumFail error for when an enum validation fails
func EnumFail(name, in string, value interface{}, values []interface{}) *Validation {
var msg string
if in == "" {
msg = fmt.Sprintf(enumFailNoIn, name, values)
} else {
msg = fmt.Sprintf(enumFail, name, in, values)
}
return &Validation{
code: EnumFailCode,
Name: name,
In: in,
Value: value,
Values: values,
message: msg,
}
}
// Required error for when a value is missing
func Required(name, in string, value interface{}) *Validation {
var msg string
if in == "" {
msg = fmt.Sprintf(requiredFailNoIn, name)
} else {
msg = fmt.Sprintf(requiredFail, name, in)
}
return &Validation{
code: RequiredFailCode,
Name: name,
In: in,
Value: value,
message: msg,
}
}
// ReadOnly error for when a value is present in request
func ReadOnly(name, in string, value interface{}) *Validation {
var msg string
if in == "" {
msg = fmt.Sprintf(readOnlyFailNoIn, name)
} else {
msg = fmt.Sprintf(readOnlyFail, name, in)
}
return &Validation{
code: ReadOnlyFailCode,
Name: name,
In: in,
Value: value,
message: msg,
}
}
// TooLong error for when a string is too long
func TooLong(name, in string, maximum int64, value interface{}) *Validation {
var msg string
if in == "" {
msg = fmt.Sprintf(tooLongMessageNoIn, name, maximum)
} else {
msg = fmt.Sprintf(tooLongMessage, name, in, maximum)
}
return &Validation{
code: TooLongFailCode,
Name: name,
In: in,
Value: value,
message: msg,
}
}
// TooShort error for when a string is too short
func TooShort(name, in string, minimum int64, value interface{}) *Validation {
var msg string
if in == "" {
msg = fmt.Sprintf(tooShortMessageNoIn, name, minimum)
} else {
msg = fmt.Sprintf(tooShortMessage, name, in, minimum)
}
return &Validation{
code: TooShortFailCode,
Name: name,
In: in,
Value: value,
message: msg,
}
}
// FailedPattern error for when a string fails a regex pattern match
// the pattern that is returned is the ECMA syntax version of the pattern not the golang version.
func FailedPattern(name, in, pattern string, value interface{}) *Validation {
var msg string
if in == "" {
msg = fmt.Sprintf(patternFailNoIn, name, pattern)
} else {
msg = fmt.Sprintf(patternFail, name, in, pattern)
}
return &Validation{
code: PatternFailCode,
Name: name,
In: in,
Value: value,
message: msg,
}
}
// MultipleOfMustBePositive error for when a
// multipleOf factor is negative
func MultipleOfMustBePositive(name, in string, factor interface{}) *Validation {
return &Validation{
code: MultipleOfMustBePositiveCode,
Name: name,
In: in,
Value: factor,
message: fmt.Sprintf(multipleOfMustBePositive, name, factor),
}
}

View file

@ -1 +0,0 @@
secrets.yml

View file

@ -1,61 +0,0 @@
linters-settings:
govet:
check-shadowing: true
golint:
min-confidence: 0
gocyclo:
min-complexity: 45
maligned:
suggest-new: true
dupl:
threshold: 200
goconst:
min-len: 2
min-occurrences: 3
linters:
enable-all: true
disable:
- maligned
- unparam
- lll
- gochecknoinits
- gochecknoglobals
- funlen
- godox
- gocognit
- whitespace
- wsl
- wrapcheck
- testpackage
- nlreturn
- gomnd
- exhaustivestruct
- goerr113
- errorlint
- nestif
- godot
- gofumpt
- paralleltest
- tparallel
- thelper
- ifshort
- exhaustruct
- varnamelen
- gci
- depguard
- errchkjson
- inamedparam
- nonamedreturns
- musttag
- ireturn
- forcetypeassert
- cyclop
# deprecated linters
- deadcode
- interfacer
- scopelint
- varcheck
- structcheck
- golint
- nosnakecase

View file

@ -1,74 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
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
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
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
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.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
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]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View file

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1,19 +0,0 @@
# gojsonreference [![Build Status](https://github.com/go-openapi/jsonreference/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/jsonreference/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/jsonreference/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonreference)
[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonreference/master/LICENSE)
[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/jsonreference.svg)](https://pkg.go.dev/github.com/go-openapi/jsonreference)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/jsonreference)](https://goreportcard.com/report/github.com/go-openapi/jsonreference)
An implementation of JSON Reference - Go language
## Status
Feature complete. Stable API
## Dependencies
* https://github.com/go-openapi/jsonpointer
## References
* http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07
* http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03

View file

@ -1,69 +0,0 @@
package internal
import (
"net/url"
"regexp"
"strings"
)
const (
defaultHTTPPort = ":80"
defaultHTTPSPort = ":443"
)
// Regular expressions used by the normalizations
var rxPort = regexp.MustCompile(`(:\d+)/?$`)
var rxDupSlashes = regexp.MustCompile(`/{2,}`)
// NormalizeURL will normalize the specified URL
// This was added to replace a previous call to the no longer maintained purell library:
// The call that was used looked like the following:
//
// url.Parse(purell.NormalizeURL(parsed, purell.FlagsSafe|purell.FlagRemoveDuplicateSlashes))
//
// To explain all that was included in the call above, purell.FlagsSafe was really just the following:
// - FlagLowercaseScheme
// - FlagLowercaseHost
// - FlagRemoveDefaultPort
// - FlagRemoveDuplicateSlashes (and this was mixed in with the |)
//
// This also normalizes the URL into its urlencoded form by removing RawPath and RawFragment.
func NormalizeURL(u *url.URL) {
lowercaseScheme(u)
lowercaseHost(u)
removeDefaultPort(u)
removeDuplicateSlashes(u)
u.RawPath = ""
u.RawFragment = ""
}
func lowercaseScheme(u *url.URL) {
if len(u.Scheme) > 0 {
u.Scheme = strings.ToLower(u.Scheme)
}
}
func lowercaseHost(u *url.URL) {
if len(u.Host) > 0 {
u.Host = strings.ToLower(u.Host)
}
}
func removeDefaultPort(u *url.URL) {
if len(u.Host) > 0 {
scheme := strings.ToLower(u.Scheme)
u.Host = rxPort.ReplaceAllStringFunc(u.Host, func(val string) string {
if (scheme == "http" && val == defaultHTTPPort) || (scheme == "https" && val == defaultHTTPSPort) {
return ""
}
return val
})
}
}
func removeDuplicateSlashes(u *url.URL) {
if len(u.Path) > 0 {
u.Path = rxDupSlashes.ReplaceAllString(u.Path, "/")
}
}

View file

@ -1,158 +0,0 @@
// Copyright 2013 sigu-399 ( https://github.com/sigu-399 )
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// author sigu-399
// author-github https://github.com/sigu-399
// author-mail sigu.399@gmail.com
//
// repository-name jsonreference
// repository-desc An implementation of JSON Reference - Go language
//
// description Main and unique file.
//
// created 26-02-2013
package jsonreference
import (
"errors"
"net/url"
"strings"
"github.com/go-openapi/jsonpointer"
"github.com/go-openapi/jsonreference/internal"
)
const (
fragmentRune = `#`
)
// New creates a new reference for the given string
func New(jsonReferenceString string) (Ref, error) {
var r Ref
err := r.parse(jsonReferenceString)
return r, err
}
// MustCreateRef parses the ref string and panics when it's invalid.
// Use the New method for a version that returns an error
func MustCreateRef(ref string) Ref {
r, err := New(ref)
if err != nil {
panic(err)
}
return r
}
// Ref represents a json reference object
type Ref struct {
referenceURL *url.URL
referencePointer jsonpointer.Pointer
HasFullURL bool
HasURLPathOnly bool
HasFragmentOnly bool
HasFileScheme bool
HasFullFilePath bool
}
// GetURL gets the URL for this reference
func (r *Ref) GetURL() *url.URL {
return r.referenceURL
}
// GetPointer gets the json pointer for this reference
func (r *Ref) GetPointer() *jsonpointer.Pointer {
return &r.referencePointer
}
// String returns the best version of the url for this reference
func (r *Ref) String() string {
if r.referenceURL != nil {
return r.referenceURL.String()
}
if r.HasFragmentOnly {
return fragmentRune + r.referencePointer.String()
}
return r.referencePointer.String()
}
// IsRoot returns true if this reference is a root document
func (r *Ref) IsRoot() bool {
return r.referenceURL != nil &&
!r.IsCanonical() &&
!r.HasURLPathOnly &&
r.referenceURL.Fragment == ""
}
// IsCanonical returns true when this pointer starts with http(s):// or file://
func (r *Ref) IsCanonical() bool {
return (r.HasFileScheme && r.HasFullFilePath) || (!r.HasFileScheme && r.HasFullURL)
}
// "Constructor", parses the given string JSON reference
func (r *Ref) parse(jsonReferenceString string) error {
parsed, err := url.Parse(jsonReferenceString)
if err != nil {
return err
}
internal.NormalizeURL(parsed)
r.referenceURL = parsed
refURL := r.referenceURL
if refURL.Scheme != "" && refURL.Host != "" {
r.HasFullURL = true
} else {
if refURL.Path != "" {
r.HasURLPathOnly = true
} else if refURL.RawQuery == "" && refURL.Fragment != "" {
r.HasFragmentOnly = true
}
}
r.HasFileScheme = refURL.Scheme == "file"
r.HasFullFilePath = strings.HasPrefix(refURL.Path, "/")
// invalid json-pointer error means url has no json-pointer fragment. simply ignore error
r.referencePointer, _ = jsonpointer.New(refURL.Fragment)
return nil
}
// Inherits creates a new reference from a parent and a child
// If the child cannot inherit from the parent, an error is returned
func (r *Ref) Inherits(child Ref) (*Ref, error) {
childURL := child.GetURL()
parentURL := r.GetURL()
if childURL == nil {
return nil, errors.New("child url is nil")
}
if parentURL == nil {
return &child, nil
}
ref, err := New(parentURL.ResolveReference(childURL).String())
if err != nil {
return nil, err
}
return &ref, nil
}

View file

@ -1,26 +0,0 @@
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
# Set default charset
[*.{js,py,go,scala,rb,java,html,css,less,sass,md}]
charset = utf-8
# Tab indentation (no size specified)
[*.go]
indent_style = tab
[*.md]
trim_trailing_whitespace = false
# Matches the exact files either package.json or .travis.yml
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2

View file

@ -1,4 +0,0 @@
secrets.yml
coverage.out
profile.cov
profile.out

View file

@ -1,61 +0,0 @@
linters-settings:
govet:
check-shadowing: true
golint:
min-confidence: 0
gocyclo:
min-complexity: 45
maligned:
suggest-new: true
dupl:
threshold: 200
goconst:
min-len: 2
min-occurrences: 3
linters:
enable-all: true
disable:
- maligned
- unparam
- lll
- gochecknoinits
- gochecknoglobals
- funlen
- godox
- gocognit
- whitespace
- wsl
- wrapcheck
- testpackage
- nlreturn
- gomnd
- exhaustivestruct
- goerr113
- errorlint
- nestif
- godot
- gofumpt
- paralleltest
- tparallel
- thelper
- ifshort
- exhaustruct
- varnamelen
- gci
- depguard
- errchkjson
- inamedparam
- nonamedreturns
- musttag
- ireturn
- forcetypeassert
- cyclop
# deprecated linters
- deadcode
- interfacer
- scopelint
- varcheck
- structcheck
- golint
- nosnakecase

View file

@ -1,25 +0,0 @@
after_success:
- bash <(curl -s https://codecov.io/bash)
go:
- 1.16.x
- 1.x
install:
- go get gotest.tools/gotestsum
language: go
arch:
- amd64
- ppc64le
jobs:
include:
# include linting job, but only for latest go version and amd64 arch
- go: 1.x
arch: amd64
install:
go get github.com/golangci/golangci-lint/cmd/golangci-lint
script:
- golangci-lint run --new-from-rev master
notifications:
slack:
secure: OxkPwVp35qBTUilgWC8xykSj+sGMcj0h8IIOKD+Rflx2schZVlFfdYdyVBM+s9OqeOfvtuvnR9v1Ye2rPKAvcjWdC4LpRGUsgmItZaI6Um8Aj6+K9udCw5qrtZVfOVmRu8LieH//XznWWKdOultUuniW0MLqw5+II87Gd00RWbCGi0hk0PykHe7uK+PDA2BEbqyZ2WKKYCvfB3j+0nrFOHScXqnh0V05l2E83J4+Sgy1fsPy+1WdX58ZlNBG333ibaC1FS79XvKSmTgKRkx3+YBo97u6ZtUmJa5WZjf2OdLG3KIckGWAv6R5xgxeU31N0Ng8L332w/Edpp2O/M2bZwdnKJ8hJQikXIAQbICbr+lTDzsoNzMdEIYcHpJ5hjPbiUl3Bmd+Jnsjf5McgAZDiWIfpCKZ29tPCEkVwRsOCqkyPRMNMzHHmoja495P5jR+ODS7+J8RFg5xgcnOgpP9D4Wlhztlf5WyZMpkLxTUD+bZq2SRf50HfHFXTkfq22zPl3d1eq0yrLwh/Z/fWKkfb6SyysROL8y6s8u3dpFX1YHSg0BR6i913h4aoZw9B2BG27cafLLTwKYsp2dFo1PWl4O6u9giFJIeqwloZHLKKrwh0cBFhB7RH0I58asxkZpCH6uWjJierahmHe7iS+E6i+9oCHkOZ59hmCYNimIs3hM=
script:
- gotestsum -f short-verbose -- -race -timeout=20m -coverprofile=coverage.txt -covermode=atomic ./...

View file

@ -1,74 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
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
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
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
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.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
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]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View file

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1,6 +0,0 @@
# Loads OAI specs [![Build Status](https://github.com/go-openapi/loads/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/loads/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/loads/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/loads)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/loads/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/loads?status.svg)](http://godoc.org/github.com/go-openapi/loads)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/loads)](https://goreportcard.com/report/github.com/go-openapi/loads)
Loading of OAI specification documents from local or remote locations. Supports JSON and YAML documents.

View file

@ -1,18 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package loads provides document loading methods for swagger (OAI) specifications.
//
// It is used by other go-openapi packages to load and run analysis on local or remote spec documents.
package loads

View file

@ -1,133 +0,0 @@
package loads
import (
"encoding/json"
"errors"
"net/url"
"github.com/go-openapi/spec"
"github.com/go-openapi/swag"
)
var (
// Default chain of loaders, defined at the package level.
//
// By default this matches json and yaml documents.
//
// May be altered with AddLoader().
loaders *loader
)
func init() {
jsonLoader := &loader{
DocLoaderWithMatch: DocLoaderWithMatch{
Match: func(_ string) bool {
return true
},
Fn: JSONDoc,
},
}
loaders = jsonLoader.WithHead(&loader{
DocLoaderWithMatch: DocLoaderWithMatch{
Match: swag.YAMLMatcher,
Fn: swag.YAMLDoc,
},
})
// sets the global default loader for go-openapi/spec
spec.PathLoader = loaders.Load
}
// DocLoader represents a doc loader type
type DocLoader func(string) (json.RawMessage, error)
// DocMatcher represents a predicate to check if a loader matches
type DocMatcher func(string) bool
// DocLoaderWithMatch describes a loading function for a given extension match.
type DocLoaderWithMatch struct {
Fn DocLoader
Match DocMatcher
}
// NewDocLoaderWithMatch builds a DocLoaderWithMatch to be used in load options
func NewDocLoaderWithMatch(fn DocLoader, matcher DocMatcher) DocLoaderWithMatch {
return DocLoaderWithMatch{
Fn: fn,
Match: matcher,
}
}
type loader struct {
DocLoaderWithMatch
Next *loader
}
// WithHead adds a loader at the head of the current stack
func (l *loader) WithHead(head *loader) *loader {
if head == nil {
return l
}
head.Next = l
return head
}
// WithNext adds a loader at the trail of the current stack
func (l *loader) WithNext(next *loader) *loader {
l.Next = next
return next
}
// Load the raw document from path
func (l *loader) Load(path string) (json.RawMessage, error) {
_, erp := url.Parse(path)
if erp != nil {
return nil, erp
}
lastErr := errors.New("no loader matched") // default error if no match was found
for ldr := l; ldr != nil; ldr = ldr.Next {
if ldr.Match != nil && !ldr.Match(path) {
continue
}
// try then move to next one if there is an error
b, err := ldr.Fn(path)
if err == nil {
return b, nil
}
lastErr = err
}
return nil, lastErr
}
// JSONDoc loads a json document from either a file or a remote url
func JSONDoc(path string) (json.RawMessage, error) {
data, err := swag.LoadFromFileOrHTTP(path)
if err != nil {
return nil, err
}
return json.RawMessage(data), nil
}
// AddLoader for a document, executed before other previously set loaders.
//
// This sets the configuration at the package level.
//
// NOTE:
// - this updates the default loader used by github.com/go-openapi/spec
// - since this sets package level globals, you shouln't call this concurrently
func AddLoader(predicate DocMatcher, load DocLoader) {
loaders = loaders.WithHead(&loader{
DocLoaderWithMatch: DocLoaderWithMatch{
Match: predicate,
Fn: load,
},
})
// sets the global default loader for go-openapi/spec
spec.PathLoader = loaders.Load
}

View file

@ -1,61 +0,0 @@
package loads
type options struct {
loader *loader
}
func defaultOptions() *options {
return &options{
loader: loaders,
}
}
func loaderFromOptions(options []LoaderOption) *loader {
opts := defaultOptions()
for _, apply := range options {
apply(opts)
}
return opts.loader
}
// LoaderOption allows to fine-tune the spec loader behavior
type LoaderOption func(*options)
// WithDocLoader sets a custom loader for loading specs
func WithDocLoader(l DocLoader) LoaderOption {
return func(opt *options) {
if l == nil {
return
}
opt.loader = &loader{
DocLoaderWithMatch: DocLoaderWithMatch{
Fn: l,
},
}
}
}
// WithDocLoaderMatches sets a chain of custom loaders for loading specs
// for different extension matches.
//
// Loaders are executed in the order of provided DocLoaderWithMatch'es.
func WithDocLoaderMatches(l ...DocLoaderWithMatch) LoaderOption {
return func(opt *options) {
var final, prev *loader
for _, ldr := range l {
if ldr.Fn == nil {
continue
}
if prev == nil {
final = &loader{DocLoaderWithMatch: ldr}
prev = final
continue
}
prev = prev.WithNext(&loader{DocLoaderWithMatch: ldr})
}
opt.loader = final
}
}

View file

@ -1,275 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package loads
import (
"bytes"
"encoding/gob"
"encoding/json"
"fmt"
"github.com/go-openapi/analysis"
"github.com/go-openapi/spec"
"github.com/go-openapi/swag"
)
func init() {
gob.Register(map[string]interface{}{})
gob.Register([]interface{}{})
}
// Document represents a swagger spec document
type Document struct {
// specAnalyzer
Analyzer *analysis.Spec
spec *spec.Swagger
specFilePath string
origSpec *spec.Swagger
schema *spec.Schema
pathLoader *loader
raw json.RawMessage
}
// JSONSpec loads a spec from a json document
func JSONSpec(path string, options ...LoaderOption) (*Document, error) {
data, err := JSONDoc(path)
if err != nil {
return nil, err
}
// convert to json
doc, err := Analyzed(data, "", options...)
if err != nil {
return nil, err
}
doc.specFilePath = path
return doc, nil
}
// Embedded returns a Document based on embedded specs. No analysis is required
func Embedded(orig, flat json.RawMessage, options ...LoaderOption) (*Document, error) {
var origSpec, flatSpec spec.Swagger
if err := json.Unmarshal(orig, &origSpec); err != nil {
return nil, err
}
if err := json.Unmarshal(flat, &flatSpec); err != nil {
return nil, err
}
return &Document{
raw: orig,
origSpec: &origSpec,
spec: &flatSpec,
pathLoader: loaderFromOptions(options),
}, nil
}
// Spec loads a new spec document from a local or remote path
func Spec(path string, options ...LoaderOption) (*Document, error) {
ldr := loaderFromOptions(options)
b, err := ldr.Load(path)
if err != nil {
return nil, err
}
document, err := Analyzed(b, "", options...)
if err != nil {
return nil, err
}
document.specFilePath = path
document.pathLoader = ldr
return document, nil
}
// Analyzed creates a new analyzed spec document for a root json.RawMessage.
func Analyzed(data json.RawMessage, version string, options ...LoaderOption) (*Document, error) {
if version == "" {
version = "2.0"
}
if version != "2.0" {
return nil, fmt.Errorf("spec version %q is not supported", version)
}
raw, err := trimData(data) // trim blanks, then convert yaml docs into json
if err != nil {
return nil, err
}
swspec := new(spec.Swagger)
if err = json.Unmarshal(raw, swspec); err != nil {
return nil, err
}
origsqspec, err := cloneSpec(swspec)
if err != nil {
return nil, err
}
d := &Document{
Analyzer: analysis.New(swspec), // NOTE: at this moment, analysis does not follow $refs to documents outside the root doc
schema: spec.MustLoadSwagger20Schema(),
spec: swspec,
raw: raw,
origSpec: origsqspec,
pathLoader: loaderFromOptions(options),
}
return d, nil
}
func trimData(in json.RawMessage) (json.RawMessage, error) {
trimmed := bytes.TrimSpace(in)
if len(trimmed) == 0 {
return in, nil
}
if trimmed[0] == '{' || trimmed[0] == '[' {
return trimmed, nil
}
// assume yaml doc: convert it to json
yml, err := swag.BytesToYAMLDoc(trimmed)
if err != nil {
return nil, fmt.Errorf("analyzed: %v", err)
}
d, err := swag.YAMLToJSON(yml)
if err != nil {
return nil, fmt.Errorf("analyzed: %v", err)
}
return d, nil
}
// Expanded expands the $ref fields in the spec document and returns a new spec document
func (d *Document) Expanded(options ...*spec.ExpandOptions) (*Document, error) {
swspec := new(spec.Swagger)
if err := json.Unmarshal(d.raw, swspec); err != nil {
return nil, err
}
var expandOptions *spec.ExpandOptions
if len(options) > 0 {
expandOptions = options[0]
if expandOptions.RelativeBase == "" {
expandOptions.RelativeBase = d.specFilePath
}
} else {
expandOptions = &spec.ExpandOptions{
RelativeBase: d.specFilePath,
}
}
if expandOptions.PathLoader == nil {
if d.pathLoader != nil {
// use loader from Document options
expandOptions.PathLoader = d.pathLoader.Load
} else {
// use package level loader
expandOptions.PathLoader = loaders.Load
}
}
if err := spec.ExpandSpec(swspec, expandOptions); err != nil {
return nil, err
}
dd := &Document{
Analyzer: analysis.New(swspec),
spec: swspec,
specFilePath: d.specFilePath,
schema: spec.MustLoadSwagger20Schema(),
raw: d.raw,
origSpec: d.origSpec,
}
return dd, nil
}
// BasePath the base path for the API specified by this spec
func (d *Document) BasePath() string {
return d.spec.BasePath
}
// Version returns the version of this spec
func (d *Document) Version() string {
return d.spec.Swagger
}
// Schema returns the swagger 2.0 schema
func (d *Document) Schema() *spec.Schema {
return d.schema
}
// Spec returns the swagger spec object model
func (d *Document) Spec() *spec.Swagger {
return d.spec
}
// Host returns the host for the API
func (d *Document) Host() string {
return d.spec.Host
}
// Raw returns the raw swagger spec as json bytes
func (d *Document) Raw() json.RawMessage {
return d.raw
}
// OrigSpec yields the original spec
func (d *Document) OrigSpec() *spec.Swagger {
return d.origSpec
}
// ResetDefinitions gives a shallow copy with the models reset to the original spec
func (d *Document) ResetDefinitions() *Document {
defs := make(map[string]spec.Schema, len(d.origSpec.Definitions))
for k, v := range d.origSpec.Definitions {
defs[k] = v
}
d.spec.Definitions = defs
return d
}
// Pristine creates a new pristine document instance based on the input data
func (d *Document) Pristine() *Document {
raw, _ := json.Marshal(d.Spec())
dd, _ := Analyzed(raw, d.Version())
dd.pathLoader = d.pathLoader
dd.specFilePath = d.specFilePath
return dd
}
// SpecFilePath returns the file path of the spec if one is defined
func (d *Document) SpecFilePath() string {
return d.specFilePath
}
func cloneSpec(src *spec.Swagger) (*spec.Swagger, error) {
var b bytes.Buffer
if err := gob.NewEncoder(&b).Encode(src); err != nil {
return nil, err
}
var dst spec.Swagger
if err := gob.NewDecoder(&b).Decode(&dst); err != nil {
return nil, err
}
return &dst, nil
}

View file

@ -1,26 +0,0 @@
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
# Set default charset
[*.{js,py,go,scala,rb,java,html,css,less,sass,md}]
charset = utf-8
# Tab indentation (no size specified)
[*.go]
indent_style = tab
[*.md]
trim_trailing_whitespace = false
# Matches the exact files either package.json or .travis.yml
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2

View file

@ -1 +0,0 @@
*.go text eol=lf

View file

@ -1,5 +0,0 @@
secrets.yml
coverage.out
*.cov
*.out
playground

View file

@ -1,62 +0,0 @@
linters-settings:
govet:
check-shadowing: true
golint:
min-confidence: 0
gocyclo:
min-complexity: 45
maligned:
suggest-new: true
dupl:
threshold: 200
goconst:
min-len: 2
min-occurrences: 3
linters:
enable-all: true
disable:
- nilerr # nilerr crashes on this repo
- maligned
- unparam
- lll
- gochecknoinits
- gochecknoglobals
- funlen
- godox
- gocognit
- whitespace
- wsl
- wrapcheck
- testpackage
- nlreturn
- gomnd
- exhaustivestruct
- goerr113
- errorlint
- nestif
- godot
- gofumpt
- paralleltest
- tparallel
- thelper
- ifshort
- exhaustruct
- varnamelen
- gci
- depguard
- errchkjson
- inamedparam
- nonamedreturns
- musttag
- ireturn
- forcetypeassert
- cyclop
# deprecated linters
- deadcode
- interfacer
- scopelint
- varcheck
- structcheck
- golint
- nosnakecase

View file

@ -1,74 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
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
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
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
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.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
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]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View file

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1,10 +0,0 @@
# runtime [![Build Status](https://github.com/go-openapi/runtime/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/runtime/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/runtime/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/runtime)
[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/runtime/master/LICENSE)
[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/runtime.svg)](https://pkg.go.dev/github.com/go-openapi/runtime)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/runtime)](https://goreportcard.com/report/github.com/go-openapi/runtime)
# go OpenAPI toolkit runtime
The runtime component for use in code generation or as untyped usage.

View file

@ -1,222 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package runtime
import (
"bytes"
"encoding"
"errors"
"fmt"
"io"
"reflect"
"github.com/go-openapi/swag"
)
func defaultCloser() error { return nil }
type byteStreamOpt func(opts *byteStreamOpts)
// ClosesStream when the bytestream consumer or producer is finished
func ClosesStream(opts *byteStreamOpts) {
opts.Close = true
}
type byteStreamOpts struct {
Close bool
}
// ByteStreamConsumer creates a consumer for byte streams.
//
// The consumer consumes from a provided reader into the data passed by reference.
//
// Supported output underlying types and interfaces, prioritized in this order:
// - io.ReaderFrom (for maximum control)
// - io.Writer (performs io.Copy)
// - encoding.BinaryUnmarshaler
// - *string
// - *[]byte
func ByteStreamConsumer(opts ...byteStreamOpt) Consumer {
var vals byteStreamOpts
for _, opt := range opts {
opt(&vals)
}
return ConsumerFunc(func(reader io.Reader, data interface{}) error {
if reader == nil {
return errors.New("ByteStreamConsumer requires a reader") // early exit
}
if data == nil {
return errors.New("nil destination for ByteStreamConsumer")
}
closer := defaultCloser
if vals.Close {
if cl, isReaderCloser := reader.(io.Closer); isReaderCloser {
closer = cl.Close
}
}
defer func() {
_ = closer()
}()
if readerFrom, isReaderFrom := data.(io.ReaderFrom); isReaderFrom {
_, err := readerFrom.ReadFrom(reader)
return err
}
if writer, isDataWriter := data.(io.Writer); isDataWriter {
_, err := io.Copy(writer, reader)
return err
}
// buffers input before writing to data
var buf bytes.Buffer
_, err := buf.ReadFrom(reader)
if err != nil {
return err
}
b := buf.Bytes()
switch destinationPointer := data.(type) {
case encoding.BinaryUnmarshaler:
return destinationPointer.UnmarshalBinary(b)
case *any:
switch (*destinationPointer).(type) {
case string:
*destinationPointer = string(b)
return nil
case []byte:
*destinationPointer = b
return nil
}
default:
// check for the underlying type to be pointer to []byte or string,
if ptr := reflect.TypeOf(data); ptr.Kind() != reflect.Ptr {
return errors.New("destination must be a pointer")
}
v := reflect.Indirect(reflect.ValueOf(data))
t := v.Type()
switch {
case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8:
v.SetBytes(b)
return nil
case t.Kind() == reflect.String:
v.SetString(string(b))
return nil
}
}
return fmt.Errorf("%v (%T) is not supported by the ByteStreamConsumer, %s",
data, data, "can be resolved by supporting Writer/BinaryUnmarshaler interface")
})
}
// ByteStreamProducer creates a producer for byte streams.
//
// The producer takes input data then writes to an output writer (essentially as a pipe).
//
// Supported input underlying types and interfaces, prioritized in this order:
// - io.WriterTo (for maximum control)
// - io.Reader (performs io.Copy). A ReadCloser is closed before exiting.
// - encoding.BinaryMarshaler
// - error (writes as a string)
// - []byte
// - string
// - struct, other slices: writes as JSON
func ByteStreamProducer(opts ...byteStreamOpt) Producer {
var vals byteStreamOpts
for _, opt := range opts {
opt(&vals)
}
return ProducerFunc(func(writer io.Writer, data interface{}) error {
if writer == nil {
return errors.New("ByteStreamProducer requires a writer") // early exit
}
if data == nil {
return errors.New("nil data for ByteStreamProducer")
}
closer := defaultCloser
if vals.Close {
if cl, isWriterCloser := writer.(io.Closer); isWriterCloser {
closer = cl.Close
}
}
defer func() {
_ = closer()
}()
if rc, isDataCloser := data.(io.ReadCloser); isDataCloser {
defer rc.Close()
}
switch origin := data.(type) {
case io.WriterTo:
_, err := origin.WriteTo(writer)
return err
case io.Reader:
_, err := io.Copy(writer, origin)
return err
case encoding.BinaryMarshaler:
bytes, err := origin.MarshalBinary()
if err != nil {
return err
}
_, err = writer.Write(bytes)
return err
case error:
_, err := writer.Write([]byte(origin.Error()))
return err
default:
v := reflect.Indirect(reflect.ValueOf(data))
t := v.Type()
switch {
case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8:
_, err := writer.Write(v.Bytes())
return err
case t.Kind() == reflect.String:
_, err := writer.Write([]byte(v.String()))
return err
case t.Kind() == reflect.Struct || t.Kind() == reflect.Slice:
b, err := swag.WriteJSON(data)
if err != nil {
return err
}
_, err = writer.Write(b)
return err
}
}
return fmt.Errorf("%v (%T) is not supported by the ByteStreamProducer, %s",
data, data, "can be resolved by supporting Reader/BinaryMarshaler interface")
})
}

View file

@ -1,30 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package runtime
import "github.com/go-openapi/strfmt"
// A ClientAuthInfoWriterFunc converts a function to a request writer interface
type ClientAuthInfoWriterFunc func(ClientRequest, strfmt.Registry) error
// AuthenticateRequest adds authentication data to the request
func (fn ClientAuthInfoWriterFunc) AuthenticateRequest(req ClientRequest, reg strfmt.Registry) error {
return fn(req, reg)
}
// A ClientAuthInfoWriter implementor knows how to write authentication info to a request
type ClientAuthInfoWriter interface {
AuthenticateRequest(ClientRequest, strfmt.Registry) error
}

View file

@ -1,41 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package runtime
import (
"context"
"net/http"
)
// ClientOperation represents the context for a swagger operation to be submitted to the transport
type ClientOperation struct {
ID string
Method string
PathPattern string
ProducesMediaTypes []string
ConsumesMediaTypes []string
Schemes []string
AuthInfo ClientAuthInfoWriter
Params ClientRequestWriter
Reader ClientResponseReader
Context context.Context //nolint:containedctx // we precisely want this type to contain the request context
Client *http.Client
}
// A ClientTransport implementor knows how to submit Request objects to some destination
type ClientTransport interface {
// Submit(string, RequestWriter, ResponseReader, AuthInfoWriter) (interface{}, error)
Submit(*ClientOperation) (interface{}, error)
}

View file

@ -1,152 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package runtime
import (
"io"
"net/http"
"net/url"
"time"
"github.com/go-openapi/strfmt"
)
// ClientRequestWriterFunc converts a function to a request writer interface
type ClientRequestWriterFunc func(ClientRequest, strfmt.Registry) error
// WriteToRequest adds data to the request
func (fn ClientRequestWriterFunc) WriteToRequest(req ClientRequest, reg strfmt.Registry) error {
return fn(req, reg)
}
// ClientRequestWriter is an interface for things that know how to write to a request
type ClientRequestWriter interface {
WriteToRequest(ClientRequest, strfmt.Registry) error
}
// ClientRequest is an interface for things that know how to
// add information to a swagger client request.
type ClientRequest interface { //nolint:interfacebloat // a swagger-capable request is quite rich, hence the many getter/setters
SetHeaderParam(string, ...string) error
GetHeaderParams() http.Header
SetQueryParam(string, ...string) error
SetFormParam(string, ...string) error
SetPathParam(string, string) error
GetQueryParams() url.Values
SetFileParam(string, ...NamedReadCloser) error
SetBodyParam(interface{}) error
SetTimeout(time.Duration) error
GetMethod() string
GetPath() string
GetBody() []byte
GetBodyParam() interface{}
GetFileParam() map[string][]NamedReadCloser
}
// NamedReadCloser represents a named ReadCloser interface
type NamedReadCloser interface {
io.ReadCloser
Name() string
}
// NamedReader creates a NamedReadCloser for use as file upload
func NamedReader(name string, rdr io.Reader) NamedReadCloser {
rc, ok := rdr.(io.ReadCloser)
if !ok {
rc = io.NopCloser(rdr)
}
return &namedReadCloser{
name: name,
cr: rc,
}
}
type namedReadCloser struct {
name string
cr io.ReadCloser
}
func (n *namedReadCloser) Close() error {
return n.cr.Close()
}
func (n *namedReadCloser) Read(p []byte) (int, error) {
return n.cr.Read(p)
}
func (n *namedReadCloser) Name() string {
return n.name
}
type TestClientRequest struct {
Headers http.Header
Body interface{}
}
func (t *TestClientRequest) SetHeaderParam(name string, values ...string) error {
if t.Headers == nil {
t.Headers = make(http.Header)
}
t.Headers.Set(name, values[0])
return nil
}
func (t *TestClientRequest) SetQueryParam(_ string, _ ...string) error { return nil }
func (t *TestClientRequest) SetFormParam(_ string, _ ...string) error { return nil }
func (t *TestClientRequest) SetPathParam(_ string, _ string) error { return nil }
func (t *TestClientRequest) SetFileParam(_ string, _ ...NamedReadCloser) error { return nil }
func (t *TestClientRequest) SetBodyParam(body interface{}) error {
t.Body = body
return nil
}
func (t *TestClientRequest) SetTimeout(time.Duration) error {
return nil
}
func (t *TestClientRequest) GetQueryParams() url.Values { return nil }
func (t *TestClientRequest) GetMethod() string { return "" }
func (t *TestClientRequest) GetPath() string { return "" }
func (t *TestClientRequest) GetBody() []byte { return nil }
func (t *TestClientRequest) GetBodyParam() interface{} {
return t.Body
}
func (t *TestClientRequest) GetFileParam() map[string][]NamedReadCloser {
return nil
}
func (t *TestClientRequest) GetHeaderParams() http.Header {
return t.Headers
}

View file

@ -1,110 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package runtime
import (
"encoding/json"
"fmt"
"io"
)
// A ClientResponse represents a client response
// This bridges between responses obtained from different transports
type ClientResponse interface {
Code() int
Message() string
GetHeader(string) string
GetHeaders(string) []string
Body() io.ReadCloser
}
// A ClientResponseReaderFunc turns a function into a ClientResponseReader interface implementation
type ClientResponseReaderFunc func(ClientResponse, Consumer) (interface{}, error)
// ReadResponse reads the response
func (read ClientResponseReaderFunc) ReadResponse(resp ClientResponse, consumer Consumer) (interface{}, error) {
return read(resp, consumer)
}
// A ClientResponseReader is an interface for things want to read a response.
// An application of this is to create structs from response values
type ClientResponseReader interface {
ReadResponse(ClientResponse, Consumer) (interface{}, error)
}
// NewAPIError creates a new API error
func NewAPIError(opName string, payload interface{}, code int) *APIError {
return &APIError{
OperationName: opName,
Response: payload,
Code: code,
}
}
// APIError wraps an error model and captures the status code
type APIError struct {
OperationName string
Response interface{}
Code int
}
func (o *APIError) Error() string {
var resp []byte
if err, ok := o.Response.(error); ok {
resp = []byte("'" + err.Error() + "'")
} else {
resp, _ = json.Marshal(o.Response)
}
return fmt.Sprintf("%s (status %d): %s", o.OperationName, o.Code, resp)
}
func (o *APIError) String() string {
return o.Error()
}
// IsSuccess returns true when this elapse o k response returns a 2xx status code
func (o *APIError) IsSuccess() bool {
return o.Code/100 == 2
}
// IsRedirect returns true when this elapse o k response returns a 3xx status code
func (o *APIError) IsRedirect() bool {
return o.Code/100 == 3
}
// IsClientError returns true when this elapse o k response returns a 4xx status code
func (o *APIError) IsClientError() bool {
return o.Code/100 == 4
}
// IsServerError returns true when this elapse o k response returns a 5xx status code
func (o *APIError) IsServerError() bool {
return o.Code/100 == 5
}
// IsCode returns true when this elapse o k response returns a 4xx status code
func (o *APIError) IsCode(code int) bool {
return o.Code == code
}
// A ClientResponseStatus is a common interface implemented by all responses on the generated code
// You can use this to treat any client response based on status code
type ClientResponseStatus interface {
IsSuccess() bool
IsRedirect() bool
IsClientError() bool
IsServerError() bool
IsCode(int) bool
}

View file

@ -1,49 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package runtime
const (
// HeaderContentType represents a http content-type header, it's value is supposed to be a mime type
HeaderContentType = "Content-Type"
// HeaderTransferEncoding represents a http transfer-encoding header.
HeaderTransferEncoding = "Transfer-Encoding"
// HeaderAccept the Accept header
HeaderAccept = "Accept"
// HeaderAuthorization the Authorization header
HeaderAuthorization = "Authorization"
charsetKey = "charset"
// DefaultMime the default fallback mime type
DefaultMime = "application/octet-stream"
// JSONMime the json mime type
JSONMime = "application/json"
// YAMLMime the yaml mime type
YAMLMime = "application/x-yaml"
// XMLMime the xml mime type
XMLMime = "application/xml"
// TextMime the text mime type
TextMime = "text/plain"
// HTMLMime the html mime type
HTMLMime = "text/html"
// CSVMime the csv mime type
CSVMime = "text/csv"
// MultipartFormMime the multipart form mime type
MultipartFormMime = "multipart/form-data"
// URLencodedFormMime the url encoded form mime type
URLencodedFormMime = "application/x-www-form-urlencoded"
)

View file

@ -1,350 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package runtime
import (
"bytes"
"context"
"encoding"
"encoding/csv"
"errors"
"fmt"
"io"
"reflect"
"golang.org/x/sync/errgroup"
)
// CSVConsumer creates a new CSV consumer.
//
// The consumer consumes CSV records from a provided reader into the data passed by reference.
//
// CSVOpts options may be specified to alter the default CSV behavior on the reader and the writer side (e.g. separator, skip header, ...).
// The defaults are those of the standard library's csv.Reader and csv.Writer.
//
// Supported output underlying types and interfaces, prioritized in this order:
// - *csv.Writer
// - CSVWriter (writer options are ignored)
// - io.Writer (as raw bytes)
// - io.ReaderFrom (as raw bytes)
// - encoding.BinaryUnmarshaler (as raw bytes)
// - *[][]string (as a collection of records)
// - *[]byte (as raw bytes)
// - *string (a raw bytes)
//
// The consumer prioritizes situations where buffering the input is not required.
func CSVConsumer(opts ...CSVOpt) Consumer {
o := csvOptsWithDefaults(opts)
return ConsumerFunc(func(reader io.Reader, data interface{}) error {
if reader == nil {
return errors.New("CSVConsumer requires a reader")
}
if data == nil {
return errors.New("nil destination for CSVConsumer")
}
csvReader := csv.NewReader(reader)
o.applyToReader(csvReader)
closer := defaultCloser
if o.closeStream {
if cl, isReaderCloser := reader.(io.Closer); isReaderCloser {
closer = cl.Close
}
}
defer func() {
_ = closer()
}()
switch destination := data.(type) {
case *csv.Writer:
csvWriter := destination
o.applyToWriter(csvWriter)
return pipeCSV(csvWriter, csvReader, o)
case CSVWriter:
csvWriter := destination
// no writer options available
return pipeCSV(csvWriter, csvReader, o)
case io.Writer:
csvWriter := csv.NewWriter(destination)
o.applyToWriter(csvWriter)
return pipeCSV(csvWriter, csvReader, o)
case io.ReaderFrom:
var buf bytes.Buffer
csvWriter := csv.NewWriter(&buf)
o.applyToWriter(csvWriter)
if err := bufferedCSV(csvWriter, csvReader, o); err != nil {
return err
}
_, err := destination.ReadFrom(&buf)
return err
case encoding.BinaryUnmarshaler:
var buf bytes.Buffer
csvWriter := csv.NewWriter(&buf)
o.applyToWriter(csvWriter)
if err := bufferedCSV(csvWriter, csvReader, o); err != nil {
return err
}
return destination.UnmarshalBinary(buf.Bytes())
default:
// support *[][]string, *[]byte, *string
if ptr := reflect.TypeOf(data); ptr.Kind() != reflect.Ptr {
return errors.New("destination must be a pointer")
}
v := reflect.Indirect(reflect.ValueOf(data))
t := v.Type()
switch {
case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Slice && t.Elem().Elem().Kind() == reflect.String:
csvWriter := &csvRecordsWriter{}
// writer options are ignored
if err := pipeCSV(csvWriter, csvReader, o); err != nil {
return err
}
v.Grow(len(csvWriter.records))
v.SetCap(len(csvWriter.records)) // in case Grow was unnessary, trim down the capacity
v.SetLen(len(csvWriter.records))
reflect.Copy(v, reflect.ValueOf(csvWriter.records))
return nil
case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8:
var buf bytes.Buffer
csvWriter := csv.NewWriter(&buf)
o.applyToWriter(csvWriter)
if err := bufferedCSV(csvWriter, csvReader, o); err != nil {
return err
}
v.SetBytes(buf.Bytes())
return nil
case t.Kind() == reflect.String:
var buf bytes.Buffer
csvWriter := csv.NewWriter(&buf)
o.applyToWriter(csvWriter)
if err := bufferedCSV(csvWriter, csvReader, o); err != nil {
return err
}
v.SetString(buf.String())
return nil
default:
return fmt.Errorf("%v (%T) is not supported by the CSVConsumer, %s",
data, data, "can be resolved by supporting CSVWriter/Writer/BinaryUnmarshaler interface",
)
}
}
})
}
// CSVProducer creates a new CSV producer.
//
// The producer takes input data then writes as CSV to an output writer (essentially as a pipe).
//
// Supported input underlying types and interfaces, prioritized in this order:
// - *csv.Reader
// - CSVReader (reader options are ignored)
// - io.Reader
// - io.WriterTo
// - encoding.BinaryMarshaler
// - [][]string
// - []byte
// - string
//
// The producer prioritizes situations where buffering the input is not required.
func CSVProducer(opts ...CSVOpt) Producer {
o := csvOptsWithDefaults(opts)
return ProducerFunc(func(writer io.Writer, data interface{}) error {
if writer == nil {
return errors.New("CSVProducer requires a writer")
}
if data == nil {
return errors.New("nil data for CSVProducer")
}
csvWriter := csv.NewWriter(writer)
o.applyToWriter(csvWriter)
closer := defaultCloser
if o.closeStream {
if cl, isWriterCloser := writer.(io.Closer); isWriterCloser {
closer = cl.Close
}
}
defer func() {
_ = closer()
}()
if rc, isDataCloser := data.(io.ReadCloser); isDataCloser {
defer rc.Close()
}
switch origin := data.(type) {
case *csv.Reader:
csvReader := origin
o.applyToReader(csvReader)
return pipeCSV(csvWriter, csvReader, o)
case CSVReader:
csvReader := origin
// no reader options available
return pipeCSV(csvWriter, csvReader, o)
case io.Reader:
csvReader := csv.NewReader(origin)
o.applyToReader(csvReader)
return pipeCSV(csvWriter, csvReader, o)
case io.WriterTo:
// async piping of the writes performed by WriteTo
r, w := io.Pipe()
csvReader := csv.NewReader(r)
o.applyToReader(csvReader)
pipe, _ := errgroup.WithContext(context.Background())
pipe.Go(func() error {
_, err := origin.WriteTo(w)
_ = w.Close()
return err
})
pipe.Go(func() error {
defer func() {
_ = r.Close()
}()
return pipeCSV(csvWriter, csvReader, o)
})
return pipe.Wait()
case encoding.BinaryMarshaler:
buf, err := origin.MarshalBinary()
if err != nil {
return err
}
rdr := bytes.NewBuffer(buf)
csvReader := csv.NewReader(rdr)
return bufferedCSV(csvWriter, csvReader, o)
default:
// support [][]string, []byte, string (or pointers to those)
v := reflect.Indirect(reflect.ValueOf(data))
t := v.Type()
switch {
case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Slice && t.Elem().Elem().Kind() == reflect.String:
csvReader := &csvRecordsWriter{
records: make([][]string, v.Len()),
}
reflect.Copy(reflect.ValueOf(csvReader.records), v)
return pipeCSV(csvWriter, csvReader, o)
case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8:
buf := bytes.NewBuffer(v.Bytes())
csvReader := csv.NewReader(buf)
o.applyToReader(csvReader)
return bufferedCSV(csvWriter, csvReader, o)
case t.Kind() == reflect.String:
buf := bytes.NewBufferString(v.String())
csvReader := csv.NewReader(buf)
o.applyToReader(csvReader)
return bufferedCSV(csvWriter, csvReader, o)
default:
return fmt.Errorf("%v (%T) is not supported by the CSVProducer, %s",
data, data, "can be resolved by supporting CSVReader/Reader/BinaryMarshaler interface",
)
}
}
})
}
// pipeCSV copies CSV records from a CSV reader to a CSV writer
func pipeCSV(csvWriter CSVWriter, csvReader CSVReader, opts csvOpts) error {
for ; opts.skippedLines > 0; opts.skippedLines-- {
_, err := csvReader.Read()
if err != nil {
if errors.Is(err, io.EOF) {
return nil
}
return err
}
}
for {
record, err := csvReader.Read()
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return err
}
if err := csvWriter.Write(record); err != nil {
return err
}
}
csvWriter.Flush()
return csvWriter.Error()
}
// bufferedCSV copies CSV records from a CSV reader to a CSV writer,
// by first reading all records then writing them at once.
func bufferedCSV(csvWriter *csv.Writer, csvReader *csv.Reader, opts csvOpts) error {
for ; opts.skippedLines > 0; opts.skippedLines-- {
_, err := csvReader.Read()
if err != nil {
if errors.Is(err, io.EOF) {
return nil
}
return err
}
}
records, err := csvReader.ReadAll()
if err != nil {
return err
}
return csvWriter.WriteAll(records)
}

View file

@ -1,121 +0,0 @@
package runtime
import (
"encoding/csv"
"io"
)
// CSVOpts alter the behavior of the CSV consumer or producer.
type CSVOpt func(*csvOpts)
type csvOpts struct {
csvReader csv.Reader
csvWriter csv.Writer
skippedLines int
closeStream bool
}
// WithCSVReaderOpts specifies the options to csv.Reader
// when reading CSV.
func WithCSVReaderOpts(reader csv.Reader) CSVOpt {
return func(o *csvOpts) {
o.csvReader = reader
}
}
// WithCSVWriterOpts specifies the options to csv.Writer
// when writing CSV.
func WithCSVWriterOpts(writer csv.Writer) CSVOpt {
return func(o *csvOpts) {
o.csvWriter = writer
}
}
// WithCSVSkipLines will skip header lines.
func WithCSVSkipLines(skipped int) CSVOpt {
return func(o *csvOpts) {
o.skippedLines = skipped
}
}
func WithCSVClosesStream() CSVOpt {
return func(o *csvOpts) {
o.closeStream = true
}
}
func (o csvOpts) applyToReader(in *csv.Reader) {
if o.csvReader.Comma != 0 {
in.Comma = o.csvReader.Comma
}
if o.csvReader.Comment != 0 {
in.Comment = o.csvReader.Comment
}
if o.csvReader.FieldsPerRecord != 0 {
in.FieldsPerRecord = o.csvReader.FieldsPerRecord
}
in.LazyQuotes = o.csvReader.LazyQuotes
in.TrimLeadingSpace = o.csvReader.TrimLeadingSpace
in.ReuseRecord = o.csvReader.ReuseRecord
}
func (o csvOpts) applyToWriter(in *csv.Writer) {
if o.csvWriter.Comma != 0 {
in.Comma = o.csvWriter.Comma
}
in.UseCRLF = o.csvWriter.UseCRLF
}
func csvOptsWithDefaults(opts []CSVOpt) csvOpts {
var o csvOpts
for _, apply := range opts {
apply(&o)
}
return o
}
type CSVWriter interface {
Write([]string) error
Flush()
Error() error
}
type CSVReader interface {
Read() ([]string, error)
}
var (
_ CSVWriter = &csvRecordsWriter{}
_ CSVReader = &csvRecordsWriter{}
)
// csvRecordsWriter is an internal container to move CSV records back and forth
type csvRecordsWriter struct {
i int
records [][]string
}
func (w *csvRecordsWriter) Write(record []string) error {
w.records = append(w.records, record)
return nil
}
func (w *csvRecordsWriter) Read() ([]string, error) {
if w.i >= len(w.records) {
return nil, io.EOF
}
defer func() {
w.i++
}()
return w.records[w.i], nil
}
func (w *csvRecordsWriter) Flush() {}
func (w *csvRecordsWriter) Error() error {
return nil
}

View file

@ -1,9 +0,0 @@
package runtime
import "io"
// DiscardConsumer does absolutely nothing, it's a black hole.
var DiscardConsumer = ConsumerFunc(func(_ io.Reader, _ interface{}) error { return nil })
// DiscardProducer does absolutely nothing, it's a black hole.
var DiscardProducer = ProducerFunc(func(_ io.Writer, _ interface{}) error { return nil })

View file

@ -1,19 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package runtime
import "github.com/go-openapi/swag"
type File = swag.File

View file

@ -1,45 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package runtime
import (
"mime"
"net/http"
"github.com/go-openapi/errors"
)
// ContentType parses a content type header
func ContentType(headers http.Header) (string, string, error) {
ct := headers.Get(HeaderContentType)
orig := ct
if ct == "" {
ct = DefaultMime
}
if ct == "" {
return "", "", nil
}
mt, opts, err := mime.ParseMediaType(ct)
if err != nil {
return "", "", errors.NewParseError(HeaderContentType, "header", orig, err)
}
if cs, ok := opts[charsetKey]; ok {
return mt, cs, nil
}
return mt, "", nil
}

View file

@ -1,112 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package runtime
import (
"context"
"io"
"net/http"
"github.com/go-openapi/strfmt"
)
// OperationHandlerFunc an adapter for a function to the OperationHandler interface
type OperationHandlerFunc func(interface{}) (interface{}, error)
// Handle implements the operation handler interface
func (s OperationHandlerFunc) Handle(data interface{}) (interface{}, error) {
return s(data)
}
// OperationHandler a handler for a swagger operation
type OperationHandler interface {
Handle(interface{}) (interface{}, error)
}
// ConsumerFunc represents a function that can be used as a consumer
type ConsumerFunc func(io.Reader, interface{}) error
// Consume consumes the reader into the data parameter
func (fn ConsumerFunc) Consume(reader io.Reader, data interface{}) error {
return fn(reader, data)
}
// Consumer implementations know how to bind the values on the provided interface to
// data provided by the request body
type Consumer interface {
// Consume performs the binding of request values
Consume(io.Reader, interface{}) error
}
// ProducerFunc represents a function that can be used as a producer
type ProducerFunc func(io.Writer, interface{}) error
// Produce produces the response for the provided data
func (f ProducerFunc) Produce(writer io.Writer, data interface{}) error {
return f(writer, data)
}
// Producer implementations know how to turn the provided interface into a valid
// HTTP response
type Producer interface {
// Produce writes to the http response
Produce(io.Writer, interface{}) error
}
// AuthenticatorFunc turns a function into an authenticator
type AuthenticatorFunc func(interface{}) (bool, interface{}, error)
// Authenticate authenticates the request with the provided data
func (f AuthenticatorFunc) Authenticate(params interface{}) (bool, interface{}, error) {
return f(params)
}
// Authenticator represents an authentication strategy
// implementations of Authenticator know how to authenticate the
// request data and translate that into a valid principal object or an error
type Authenticator interface {
Authenticate(interface{}) (bool, interface{}, error)
}
// AuthorizerFunc turns a function into an authorizer
type AuthorizerFunc func(*http.Request, interface{}) error
// Authorize authorizes the processing of the request for the principal
func (f AuthorizerFunc) Authorize(r *http.Request, principal interface{}) error {
return f(r, principal)
}
// Authorizer represents an authorization strategy
// implementations of Authorizer know how to authorize the principal object
// using the request data and returns error if unauthorized
type Authorizer interface {
Authorize(*http.Request, interface{}) error
}
// Validatable types implementing this interface allow customizing their validation
// this will be used instead of the reflective validation based on the spec document.
// the implementations are assumed to have been generated by the swagger tool so they should
// contain all the validations obtained from the spec
type Validatable interface {
Validate(strfmt.Registry) error
}
// ContextValidatable types implementing this interface allow customizing their validation
// this will be used instead of the reflective validation based on the spec document.
// the implementations are assumed to have been generated by the swagger tool so they should
// contain all the context validations obtained from the spec
type ContextValidatable interface {
ContextValidate(context.Context, strfmt.Registry) error
}

View file

@ -1,38 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package runtime
import (
"encoding/json"
"io"
)
// JSONConsumer creates a new JSON consumer
func JSONConsumer() Consumer {
return ConsumerFunc(func(reader io.Reader, data interface{}) error {
dec := json.NewDecoder(reader)
dec.UseNumber() // preserve number formats
return dec.Decode(data)
})
}
// JSONProducer creates a new JSON producer
func JSONProducer() Producer {
return ProducerFunc(func(writer io.Writer, data interface{}) error {
enc := json.NewEncoder(writer)
enc.SetEscapeHTML(false)
return enc.Encode(data)
})
}

View file

@ -1,149 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package runtime
import (
"bufio"
"context"
"errors"
"io"
"net/http"
"strings"
"github.com/go-openapi/swag"
)
// CanHaveBody returns true if this method can have a body
func CanHaveBody(method string) bool {
mn := strings.ToUpper(method)
return mn == "POST" || mn == "PUT" || mn == "PATCH" || mn == "DELETE"
}
// IsSafe returns true if this is a request with a safe method
func IsSafe(r *http.Request) bool {
mn := strings.ToUpper(r.Method)
return mn == "GET" || mn == "HEAD"
}
// AllowsBody returns true if the request allows for a body
func AllowsBody(r *http.Request) bool {
mn := strings.ToUpper(r.Method)
return mn != "HEAD"
}
// HasBody returns true if this method needs a content-type
func HasBody(r *http.Request) bool {
// happy case: we have a content length set
if r.ContentLength > 0 {
return true
}
if r.Header.Get("content-length") != "" {
// in this case, no Transfer-Encoding should be present
// we have a header set but it was explicitly set to 0, so we assume no body
return false
}
rdr := newPeekingReader(r.Body)
r.Body = rdr
return rdr.HasContent()
}
func newPeekingReader(r io.ReadCloser) *peekingReader {
if r == nil {
return nil
}
return &peekingReader{
underlying: bufio.NewReader(r),
orig: r,
}
}
type peekingReader struct {
underlying interface {
Buffered() int
Peek(int) ([]byte, error)
Read([]byte) (int, error)
}
orig io.ReadCloser
}
func (p *peekingReader) HasContent() bool {
if p == nil {
return false
}
if p.underlying.Buffered() > 0 {
return true
}
b, err := p.underlying.Peek(1)
if err != nil {
return false
}
return len(b) > 0
}
func (p *peekingReader) Read(d []byte) (int, error) {
if p == nil {
return 0, io.EOF
}
if p.underlying == nil {
return 0, io.ErrUnexpectedEOF
}
return p.underlying.Read(d)
}
func (p *peekingReader) Close() error {
if p.underlying == nil {
return errors.New("reader already closed")
}
p.underlying = nil
if p.orig != nil {
return p.orig.Close()
}
return nil
}
// JSONRequest creates a new http request with json headers set.
//
// It uses context.Background.
func JSONRequest(method, urlStr string, body io.Reader) (*http.Request, error) {
req, err := http.NewRequestWithContext(context.Background(), method, urlStr, body)
if err != nil {
return nil, err
}
req.Header.Add(HeaderContentType, JSONMime)
req.Header.Add(HeaderAccept, JSONMime)
return req, nil
}
// Gettable for things with a method GetOK(string) (data string, hasKey bool, hasValue bool)
type Gettable interface {
GetOK(string) ([]string, bool, bool)
}
// ReadSingleValue reads a single value from the source
func ReadSingleValue(values Gettable, name string) string {
vv, _, hv := values.GetOK(name)
if hv {
return vv[len(vv)-1]
}
return ""
}
// ReadCollectionValue reads a collection value from a string data source
func ReadCollectionValue(values Gettable, name, collectionFormat string) []string {
v := ReadSingleValue(values, name)
return swag.SplitByFormat(v, collectionFormat)
}

View file

@ -1,90 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package runtime
// Statuses lists the most common HTTP status codes to default message
// taken from https://httpstatuses.com/
var Statuses = map[int]string{
100: "Continue",
101: "Switching Protocols",
102: "Processing",
103: "Checkpoint",
122: "URI too long",
200: "OK",
201: "Created",
202: "Accepted",
203: "Request Processed",
204: "No Content",
205: "Reset Content",
206: "Partial Content",
207: "Multi-Status",
208: "Already Reported",
226: "IM Used",
300: "Multiple Choices",
301: "Moved Permanently",
302: "Found",
303: "See Other",
304: "Not Modified",
305: "Use Proxy",
306: "Switch Proxy",
307: "Temporary Redirect",
308: "Permanent Redirect",
400: "Bad Request",
401: "Unauthorized",
402: "Payment Required",
403: "Forbidden",
404: "Not Found",
405: "Method Not Allowed",
406: "Not Acceptable",
407: "Proxy Authentication Required",
408: "Request Timeout",
409: "Conflict",
410: "Gone",
411: "Length Required",
412: "Precondition Failed",
413: "Request Entity Too Large",
414: "Request-URI Too Long",
415: "Unsupported Media Type",
416: "Request Range Not Satisfiable",
417: "Expectation Failed",
418: "I'm a teapot",
420: "Enhance Your Calm",
422: "Unprocessable Entity",
423: "Locked",
424: "Failed Dependency",
426: "Upgrade Required",
428: "Precondition Required",
429: "Too Many Requests",
431: "Request Header Fields Too Large",
444: "No Response",
449: "Retry With",
450: "Blocked by Windows Parental Controls",
451: "Wrong Exchange Server",
499: "Client Closed Request",
500: "Internal Server Error",
501: "Not Implemented",
502: "Bad Gateway",
503: "Service Unavailable",
504: "Gateway Timeout",
505: "HTTP Version Not Supported",
506: "Variant Also Negotiates",
507: "Insufficient Storage",
508: "Loop Detected",
509: "Bandwidth Limit Exceeded",
510: "Not Extended",
511: "Network Authentication Required",
598: "Network read timeout error",
599: "Network connect timeout error",
}

View file

@ -1,116 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package runtime
import (
"bytes"
"encoding"
"errors"
"fmt"
"io"
"reflect"
"github.com/go-openapi/swag"
)
// TextConsumer creates a new text consumer
func TextConsumer() Consumer {
return ConsumerFunc(func(reader io.Reader, data interface{}) error {
if reader == nil {
return errors.New("TextConsumer requires a reader") // early exit
}
buf := new(bytes.Buffer)
_, err := buf.ReadFrom(reader)
if err != nil {
return err
}
b := buf.Bytes()
// If the buffer is empty, no need to unmarshal it, which causes a panic.
if len(b) == 0 {
return nil
}
if tu, ok := data.(encoding.TextUnmarshaler); ok {
err := tu.UnmarshalText(b)
if err != nil {
return fmt.Errorf("text consumer: %v", err)
}
return nil
}
t := reflect.TypeOf(data)
if data != nil && t.Kind() == reflect.Ptr {
v := reflect.Indirect(reflect.ValueOf(data))
if t.Elem().Kind() == reflect.String {
v.SetString(string(b))
return nil
}
}
return fmt.Errorf("%v (%T) is not supported by the TextConsumer, %s",
data, data, "can be resolved by supporting TextUnmarshaler interface")
})
}
// TextProducer creates a new text producer
func TextProducer() Producer {
return ProducerFunc(func(writer io.Writer, data interface{}) error {
if writer == nil {
return errors.New("TextProducer requires a writer") // early exit
}
if data == nil {
return errors.New("no data given to produce text from")
}
if tm, ok := data.(encoding.TextMarshaler); ok {
txt, err := tm.MarshalText()
if err != nil {
return fmt.Errorf("text producer: %v", err)
}
_, err = writer.Write(txt)
return err
}
if str, ok := data.(error); ok {
_, err := writer.Write([]byte(str.Error()))
return err
}
if str, ok := data.(fmt.Stringer); ok {
_, err := writer.Write([]byte(str.String()))
return err
}
v := reflect.Indirect(reflect.ValueOf(data))
if t := v.Type(); t.Kind() == reflect.Struct || t.Kind() == reflect.Slice {
b, err := swag.WriteJSON(data)
if err != nil {
return err
}
_, err = writer.Write(b)
return err
}
if v.Kind() != reflect.String {
return fmt.Errorf("%T is not a supported type by the TextProducer", data)
}
_, err := writer.Write([]byte(v.String()))
return err
})
}

View file

@ -1,19 +0,0 @@
package runtime
// Values typically represent parameters on a http request.
type Values map[string][]string
// GetOK returns the values collection for the given key.
// When the key is present in the map it will return true for hasKey.
// When the value is not empty it will return true for hasValue.
func (v Values) GetOK(key string) (value []string, hasKey bool, hasValue bool) {
value, hasKey = v[key]
if !hasKey {
return
}
if len(value) == 0 {
return
}
hasValue = true
return
}

View file

@ -1,36 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package runtime
import (
"encoding/xml"
"io"
)
// XMLConsumer creates a new XML consumer
func XMLConsumer() Consumer {
return ConsumerFunc(func(reader io.Reader, data interface{}) error {
dec := xml.NewDecoder(reader)
return dec.Decode(data)
})
}
// XMLProducer creates a new XML producer
func XMLProducer() Producer {
return ProducerFunc(func(writer io.Writer, data interface{}) error {
enc := xml.NewEncoder(writer)
return enc.Encode(data)
})
}

View file

@ -1,26 +0,0 @@
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
# Set default charset
[*.{js,py,go,scala,rb,java,html,css,less,sass,md}]
charset = utf-8
# Tab indentation (no size specified)
[*.go]
indent_style = tab
[*.md]
trim_trailing_whitespace = false
# Matches the exact files either package.json or .travis.yml
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2

View file

@ -1 +0,0 @@
*.out

View file

@ -1,61 +0,0 @@
linters-settings:
govet:
check-shadowing: true
golint:
min-confidence: 0
gocyclo:
min-complexity: 45
maligned:
suggest-new: true
dupl:
threshold: 200
goconst:
min-len: 2
min-occurrences: 3
linters:
enable-all: true
disable:
- maligned
- unparam
- lll
- gochecknoinits
- gochecknoglobals
- funlen
- godox
- gocognit
- whitespace
- wsl
- wrapcheck
- testpackage
- nlreturn
- gomnd
- exhaustivestruct
- goerr113
- errorlint
- nestif
- godot
- gofumpt
- paralleltest
- tparallel
- thelper
- ifshort
- exhaustruct
- varnamelen
- gci
- depguard
- errchkjson
- inamedparam
- nonamedreturns
- musttag
- ireturn
- forcetypeassert
- cyclop
# deprecated linters
- deadcode
- interfacer
- scopelint
- varcheck
- structcheck
- golint
- nosnakecase

View file

@ -1,74 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
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
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
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
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.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
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]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View file

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1,54 +0,0 @@
# OpenAPI v2 object model [![Build Status](https://github.com/go-openapi/spec/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/spec/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/spec/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/spec)
[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/spec/master/LICENSE)
[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/spec.svg)](https://pkg.go.dev/github.com/go-openapi/spec)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/spec)](https://goreportcard.com/report/github.com/go-openapi/spec)
The object model for OpenAPI specification documents.
### FAQ
* What does this do?
> 1. This package knows how to marshal and unmarshal Swagger API specifications into a golang object model
> 2. It knows how to resolve $ref and expand them to make a single root document
* How does it play with the rest of the go-openapi packages ?
> 1. This package is at the core of the go-openapi suite of packages and [code generator](https://github.com/go-swagger/go-swagger)
> 2. There is a [spec loading package](https://github.com/go-openapi/loads) to fetch specs as JSON or YAML from local or remote locations
> 3. There is a [spec validation package](https://github.com/go-openapi/validate) built on top of it
> 4. There is a [spec analysis package](https://github.com/go-openapi/analysis) built on top of it, to analyze, flatten, fix and merge spec documents
* Does this library support OpenAPI 3?
> No.
> This package currently only supports OpenAPI 2.0 (aka Swagger 2.0).
> 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
* Does the unmarshaling support YAML?
> Not directly. The exposed types know only how to unmarshal from JSON.
>
> 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
>
> See also https://github.com/go-openapi/spec/issues/164
* How can I validate a spec?
> 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?
> We found jsonschema compatibility more important: since `id` in jsonschema influences
> how `$ref` are resolved.
> This `id` does not conflict with any property named `id`.
>
> See also https://github.com/go-openapi/spec/issues/23

View file

@ -1,98 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package spec
import (
"sync"
)
// ResolutionCache a cache for resolving urls
type ResolutionCache interface {
Get(string) (interface{}, bool)
Set(string, interface{})
}
type simpleCache struct {
lock sync.RWMutex
store map[string]interface{}
}
func (s *simpleCache) ShallowClone() ResolutionCache {
store := make(map[string]interface{}, len(s.store))
s.lock.RLock()
for k, v := range s.store {
store[k] = v
}
s.lock.RUnlock()
return &simpleCache{
store: store,
}
}
// Get retrieves a cached URI
func (s *simpleCache) Get(uri string) (interface{}, bool) {
s.lock.RLock()
v, ok := s.store[uri]
s.lock.RUnlock()
return v, ok
}
// Set caches a URI
func (s *simpleCache) Set(uri string, data interface{}) {
s.lock.Lock()
s.store[uri] = data
s.lock.Unlock()
}
var (
// resCache is a package level cache for $ref resolution and expansion.
// It is initialized lazily by methods that have the need for it: no
// memory is allocated unless some expander methods are called.
//
// It is initialized with JSON schema and swagger schema,
// which do not mutate during normal operations.
//
// All subsequent utilizations of this cache are produced from a shallow
// clone of this initial version.
resCache *simpleCache
onceCache sync.Once
_ ResolutionCache = &simpleCache{}
)
// initResolutionCache initializes the URI resolution cache. To be wrapped in a sync.Once.Do call.
func initResolutionCache() {
resCache = defaultResolutionCache()
}
func defaultResolutionCache() *simpleCache {
return &simpleCache{store: map[string]interface{}{
"http://swagger.io/v2/schema.json": MustLoadSwagger20Schema(),
"http://json-schema.org/draft-04/schema": MustLoadJSONSchemaDraft04(),
}}
}
func cacheOrDefault(cache ResolutionCache) ResolutionCache {
onceCache.Do(initResolutionCache)
if cache != nil {
return cache
}
// get a shallow clone of the base cache with swagger and json schema
return resCache.ShallowClone()
}

View file

@ -1,57 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package spec
import (
"encoding/json"
"github.com/go-openapi/swag"
)
// ContactInfo contact information for the exposed API.
//
// For more information: http://goo.gl/8us55a#contactObject
type ContactInfo struct {
ContactInfoProps
VendorExtensible
}
// 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
func (c *ContactInfo) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &c.ContactInfoProps); err != nil {
return err
}
return json.Unmarshal(data, &c.VendorExtensible)
}
// MarshalJSON produces ContactInfo as json
func (c ContactInfo) MarshalJSON() ([]byte, error) {
b1, err := json.Marshal(c.ContactInfoProps)
if err != nil {
return nil, err
}
b2, err := json.Marshal(c.VendorExtensible)
if err != nil {
return nil, err
}
return swag.ConcatJSON(b1, b2), nil
}

View file

@ -1,49 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package spec
import (
"fmt"
"log"
"os"
"path"
"runtime"
)
// 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 (
// specLogger is a debug logger for this package
specLogger *log.Logger
)
func init() {
debugOptions()
}
func debugOptions() {
specLogger = log.New(os.Stdout, "spec:", log.LstdFlags)
}
func debugLog(msg string, args ...interface{}) {
// A private, trivial trace logger, based on go-openapi/spec/expander.go:debugLog()
if Debug {
_, file1, pos1, _ := runtime.Caller(1)
specLogger.Printf("%s:%d: %s", path.Base(file1), pos1, fmt.Sprintf(msg, args...))
}
}

View file

@ -1,17 +0,0 @@
package spec
import (
"embed"
"path"
)
//go:embed schemas/*.json schemas/*/*.json
var assets embed.FS
func jsonschemaDraft04JSONBytes() ([]byte, error) {
return assets.ReadFile(path.Join("schemas", "jsonschema-draft-04.json"))
}
func v2SchemaJSONBytes() ([]byte, error) {
return assets.ReadFile(path.Join("schemas", "v2", "schema.json"))
}

View file

@ -1,19 +0,0 @@
package spec
import "errors"
// Error codes
var (
// 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 = 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
ErrDerefUnsupportedType = errors.New("deref: unsupported 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")
)

View file

@ -1,607 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package spec
import (
"encoding/json"
"fmt"
)
// ExpandOptions provides options for the spec expander.
//
// RelativeBase is the path to the root document. This can be a remote URL or a path to a local file.
//
// If left empty, the root document is assumed to be located in the current working directory:
// all relative $ref's will be resolved from there.
//
// PathLoader injects a document loading method. By default, this resolves to the function provided by the SpecLoader package variable.
type ExpandOptions struct {
RelativeBase string // the path to the root document to expand. This is a file, not a directory
SkipSchemas bool // do not expand schemas, just paths, parameters and responses
ContinueOnError bool // continue expanding even after and error is found
PathLoader func(string) (json.RawMessage, error) `json:"-"` // the document loading method that takes a path as input and yields a json document
AbsoluteCircularRef bool // circular $ref remaining after expansion remain absolute URLs
}
func optionsOrDefault(opts *ExpandOptions) *ExpandOptions {
if opts != nil {
clone := *opts // shallow clone to avoid internal changes to be propagated to the caller
if clone.RelativeBase != "" {
clone.RelativeBase = normalizeBase(clone.RelativeBase)
}
// if the relative base is empty, let the schema loader choose a pseudo root document
return &clone
}
return &ExpandOptions{}
}
// ExpandSpec expands the references in a swagger spec
func ExpandSpec(spec *Swagger, options *ExpandOptions) error {
options = optionsOrDefault(options)
resolver := defaultSchemaLoader(spec, options, nil, nil)
specBasePath := options.RelativeBase
if !options.SkipSchemas {
for key, definition := range spec.Definitions {
parentRefs := make([]string, 0, 10)
parentRefs = append(parentRefs, "#/definitions/"+key)
def, err := expandSchema(definition, parentRefs, resolver, specBasePath)
if resolver.shouldStopOnError(err) {
return err
}
if def != nil {
spec.Definitions[key] = *def
}
}
}
for key := range spec.Parameters {
parameter := spec.Parameters[key]
if err := expandParameterOrResponse(&parameter, resolver, specBasePath); resolver.shouldStopOnError(err) {
return err
}
spec.Parameters[key] = parameter
}
for key := range spec.Responses {
response := spec.Responses[key]
if err := expandParameterOrResponse(&response, resolver, specBasePath); resolver.shouldStopOnError(err) {
return err
}
spec.Responses[key] = response
}
if spec.Paths != nil {
for key := range spec.Paths.Paths {
pth := spec.Paths.Paths[key]
if err := expandPathItem(&pth, resolver, specBasePath); resolver.shouldStopOnError(err) {
return err
}
spec.Paths.Paths[key] = pth
}
}
return nil
}
const rootBase = ".root"
// baseForRoot loads in the cache the root document and produces a fake ".root" base path entry
// for further $ref resolution
func baseForRoot(root interface{}, cache ResolutionCache) string {
// cache the root document to resolve $ref's
normalizedBase := normalizeBase(rootBase)
if root == nil {
// ensure that we never leave a nil root: always cache the root base pseudo-document
cachedRoot, found := cache.Get(normalizedBase)
if found && cachedRoot != nil {
// the cache is already preloaded with a root
return normalizedBase
}
root = map[string]interface{}{}
}
cache.Set(normalizedBase, root)
return normalizedBase
}
// ExpandSchema expands the refs in the schema object with reference to the root object.
//
// go-openapi/validate uses this function.
//
// Notice that it is impossible to reference a json schema in a different document other than root
// (use ExpandSchemaWithBasePath to resolve external references).
//
// Setting the cache is optional and this parameter may safely be left to nil.
func ExpandSchema(schema *Schema, root interface{}, cache ResolutionCache) error {
cache = cacheOrDefault(cache)
if root == nil {
root = schema
}
opts := &ExpandOptions{
// when a root is specified, cache the root as an in-memory document for $ref retrieval
RelativeBase: baseForRoot(root, cache),
SkipSchemas: false,
ContinueOnError: false,
}
return ExpandSchemaWithBasePath(schema, cache, opts)
}
// ExpandSchemaWithBasePath expands the refs in the schema object, base path configured through expand options.
//
// Setting the cache is optional and this parameter may safely be left to nil.
func ExpandSchemaWithBasePath(schema *Schema, cache ResolutionCache, opts *ExpandOptions) error {
if schema == nil {
return nil
}
cache = cacheOrDefault(cache)
opts = optionsOrDefault(opts)
resolver := defaultSchemaLoader(nil, opts, cache, nil)
parentRefs := make([]string, 0, 10)
s, err := expandSchema(*schema, parentRefs, resolver, opts.RelativeBase)
if err != nil {
return err
}
if s != nil {
// guard for when continuing on error
*schema = *s
}
return nil
}
func expandItems(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) {
if target.Items == nil {
return &target, nil
}
// array
if target.Items.Schema != nil {
t, err := expandSchema(*target.Items.Schema, parentRefs, resolver, basePath)
if err != nil {
return nil, err
}
*target.Items.Schema = *t
}
// tuple
for i := range target.Items.Schemas {
t, err := expandSchema(target.Items.Schemas[i], parentRefs, resolver, basePath)
if err != nil {
return nil, err
}
target.Items.Schemas[i] = *t
}
return &target, nil
}
func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) {
if target.Ref.String() == "" && target.Ref.IsRoot() {
newRef := normalizeRef(&target.Ref, basePath)
target.Ref = *newRef
return &target, nil
}
// change the base path of resolution when an ID is encountered
// otherwise the basePath should inherit the parent's
if target.ID != "" {
basePath, _ = resolver.setSchemaID(target, target.ID, basePath)
}
if target.Ref.String() != "" {
if !resolver.options.SkipSchemas {
return expandSchemaRef(target, parentRefs, resolver, basePath)
}
// when "expand" with SkipSchema, we just rebase the existing $ref without replacing
// the full schema.
rebasedRef, err := NewRef(normalizeURI(target.Ref.String(), basePath))
if err != nil {
return nil, err
}
target.Ref = denormalizeRef(&rebasedRef, resolver.context.basePath, resolver.context.rootID)
return &target, nil
}
for k := range target.Definitions {
tt, err := expandSchema(target.Definitions[k], parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
return &target, err
}
if tt != nil {
target.Definitions[k] = *tt
}
}
t, err := expandItems(target, parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
return &target, err
}
if t != nil {
target = *t
}
for i := range target.AllOf {
t, err := expandSchema(target.AllOf[i], parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
return &target, err
}
if t != nil {
target.AllOf[i] = *t
}
}
for i := range target.AnyOf {
t, err := expandSchema(target.AnyOf[i], parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
return &target, err
}
if t != nil {
target.AnyOf[i] = *t
}
}
for i := range target.OneOf {
t, err := expandSchema(target.OneOf[i], parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
return &target, err
}
if t != nil {
target.OneOf[i] = *t
}
}
if target.Not != nil {
t, err := expandSchema(*target.Not, parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
return &target, err
}
if t != nil {
*target.Not = *t
}
}
for k := range target.Properties {
t, err := expandSchema(target.Properties[k], parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
return &target, err
}
if t != nil {
target.Properties[k] = *t
}
}
if target.AdditionalProperties != nil && target.AdditionalProperties.Schema != nil {
t, err := expandSchema(*target.AdditionalProperties.Schema, parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
return &target, err
}
if t != nil {
*target.AdditionalProperties.Schema = *t
}
}
for k := range target.PatternProperties {
t, err := expandSchema(target.PatternProperties[k], parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
return &target, err
}
if t != nil {
target.PatternProperties[k] = *t
}
}
for k := range target.Dependencies {
if target.Dependencies[k].Schema != nil {
t, err := expandSchema(*target.Dependencies[k].Schema, parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
return &target, err
}
if t != nil {
*target.Dependencies[k].Schema = *t
}
}
}
if target.AdditionalItems != nil && target.AdditionalItems.Schema != nil {
t, err := expandSchema(*target.AdditionalItems.Schema, parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
return &target, err
}
if t != nil {
*target.AdditionalItems.Schema = *t
}
}
return &target, nil
}
func expandSchemaRef(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) {
// if a Ref is found, all sibling fields are skipped
// Ref also changes the resolution scope of children expandSchema
// here the resolution scope is changed because a $ref was encountered
normalizedRef := normalizeRef(&target.Ref, basePath)
normalizedBasePath := normalizedRef.RemoteURI()
if resolver.isCircular(normalizedRef, basePath, parentRefs...) {
// this means there is a cycle in the recursion tree: return the Ref
// - circular refs cannot be expanded. We leave them as ref.
// - denormalization means that a new local file ref is set relative to the original basePath
debugLog("short circuit circular ref: basePath: %s, normalizedPath: %s, normalized ref: %s",
basePath, normalizedBasePath, normalizedRef.String())
if !resolver.options.AbsoluteCircularRef {
target.Ref = denormalizeRef(normalizedRef, resolver.context.basePath, resolver.context.rootID)
} else {
target.Ref = *normalizedRef
}
return &target, nil
}
var t *Schema
err := resolver.Resolve(&target.Ref, &t, basePath)
if resolver.shouldStopOnError(err) {
return nil, err
}
if t == nil {
// guard for when continuing on error
return &target, nil
}
parentRefs = append(parentRefs, normalizedRef.String())
transitiveResolver := resolver.transitiveResolver(basePath, target.Ref)
basePath = resolver.updateBasePath(transitiveResolver, normalizedBasePath)
return expandSchema(*t, parentRefs, transitiveResolver, basePath)
}
func expandPathItem(pathItem *PathItem, resolver *schemaLoader, basePath string) error {
if pathItem == nil {
return nil
}
parentRefs := make([]string, 0, 10)
if err := resolver.deref(pathItem, parentRefs, basePath); resolver.shouldStopOnError(err) {
return err
}
if pathItem.Ref.String() != "" {
transitiveResolver := resolver.transitiveResolver(basePath, pathItem.Ref)
basePath = transitiveResolver.updateBasePath(resolver, basePath)
resolver = transitiveResolver
}
pathItem.Ref = Ref{}
for i := range pathItem.Parameters {
if err := expandParameterOrResponse(&(pathItem.Parameters[i]), resolver, basePath); resolver.shouldStopOnError(err) {
return err
}
}
ops := []*Operation{
pathItem.Get,
pathItem.Head,
pathItem.Options,
pathItem.Put,
pathItem.Post,
pathItem.Patch,
pathItem.Delete,
}
for _, op := range ops {
if err := expandOperation(op, resolver, basePath); resolver.shouldStopOnError(err) {
return err
}
}
return nil
}
func expandOperation(op *Operation, resolver *schemaLoader, basePath string) error {
if op == nil {
return nil
}
for i := range op.Parameters {
param := op.Parameters[i]
if err := expandParameterOrResponse(&param, resolver, basePath); resolver.shouldStopOnError(err) {
return err
}
op.Parameters[i] = param
}
if op.Responses == nil {
return nil
}
responses := op.Responses
if err := expandParameterOrResponse(responses.Default, resolver, basePath); resolver.shouldStopOnError(err) {
return err
}
for code := range responses.StatusCodeResponses {
response := responses.StatusCodeResponses[code]
if err := expandParameterOrResponse(&response, resolver, basePath); resolver.shouldStopOnError(err) {
return err
}
responses.StatusCodeResponses[code] = response
}
return nil
}
// ExpandResponseWithRoot expands a response based on a root document, not a fetchable document
//
// Notice that it is impossible to reference a json schema in a different document other than root
// (use ExpandResponse to resolve external references).
//
// Setting the cache is optional and this parameter may safely be left to nil.
func ExpandResponseWithRoot(response *Response, root interface{}, cache ResolutionCache) error {
cache = cacheOrDefault(cache)
opts := &ExpandOptions{
RelativeBase: baseForRoot(root, cache),
}
resolver := defaultSchemaLoader(root, opts, cache, nil)
return expandParameterOrResponse(response, resolver, opts.RelativeBase)
}
// ExpandResponse expands a response based on a basepath
//
// All refs inside response will be resolved relative to basePath
func ExpandResponse(response *Response, basePath string) error {
opts := optionsOrDefault(&ExpandOptions{
RelativeBase: basePath,
})
resolver := defaultSchemaLoader(nil, opts, nil, nil)
return expandParameterOrResponse(response, resolver, opts.RelativeBase)
}
// ExpandParameterWithRoot expands a parameter based on a root document, not a fetchable document.
//
// Notice that it is impossible to reference a json schema in a different document other than root
// (use ExpandParameter to resolve external references).
func ExpandParameterWithRoot(parameter *Parameter, root interface{}, cache ResolutionCache) error {
cache = cacheOrDefault(cache)
opts := &ExpandOptions{
RelativeBase: baseForRoot(root, cache),
}
resolver := defaultSchemaLoader(root, opts, cache, nil)
return expandParameterOrResponse(parameter, resolver, opts.RelativeBase)
}
// 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
func ExpandParameter(parameter *Parameter, basePath string) error {
opts := optionsOrDefault(&ExpandOptions{
RelativeBase: basePath,
})
resolver := defaultSchemaLoader(nil, opts, nil, nil)
return expandParameterOrResponse(parameter, resolver, opts.RelativeBase)
}
func getRefAndSchema(input interface{}) (*Ref, *Schema, error) {
var (
ref *Ref
sch *Schema
)
switch refable := input.(type) {
case *Parameter:
if refable == nil {
return nil, nil, nil
}
ref = &refable.Ref
sch = refable.Schema
case *Response:
if refable == nil {
return nil, nil, nil
}
ref = &refable.Ref
sch = refable.Schema
default:
return nil, nil, fmt.Errorf("unsupported type: %T: %w", input, ErrExpandUnsupportedType)
}
return ref, sch, nil
}
func expandParameterOrResponse(input interface{}, resolver *schemaLoader, basePath string) error {
ref, sch, err := getRefAndSchema(input)
if err != nil {
return err
}
if ref == nil && sch == nil { // nothing to do
return nil
}
parentRefs := make([]string, 0, 10)
if ref != nil {
// dereference this $ref
if err = resolver.deref(input, parentRefs, basePath); resolver.shouldStopOnError(err) {
return err
}
ref, sch, _ = getRefAndSchema(input)
}
if ref.String() != "" {
transitiveResolver := resolver.transitiveResolver(basePath, *ref)
basePath = resolver.updateBasePath(transitiveResolver, basePath)
resolver = transitiveResolver
}
if sch == nil {
// nothing to be expanded
if ref != nil {
*ref = Ref{}
}
return nil
}
if sch.Ref.String() != "" {
rebasedRef, ern := NewRef(normalizeURI(sch.Ref.String(), basePath))
if ern != nil {
return ern
}
if resolver.isCircular(&rebasedRef, basePath, parentRefs...) {
// this is a circular $ref: stop expansion
if !resolver.options.AbsoluteCircularRef {
sch.Ref = denormalizeRef(&rebasedRef, resolver.context.basePath, resolver.context.rootID)
} else {
sch.Ref = rebasedRef
}
}
}
// $ref expansion or rebasing is performed by expandSchema below
if ref != nil {
*ref = Ref{}
}
// expand schema
// yes, we do it even if options.SkipSchema is true: we have to go down that rabbit hole and rebase nested $ref)
s, err := expandSchema(*sch, parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
return err
}
if s != nil { // guard for when continuing on error
*sch = *s
}
return nil
}

View file

@ -1,24 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package spec
// ExternalDocumentation allows referencing an external resource for
// extended documentation.
//
// For more information: http://goo.gl/8us55a#externalDocumentationObject
type ExternalDocumentation struct {
Description string `json:"description,omitempty"`
URL string `json:"url,omitempty"`
}

View file

@ -1,203 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package spec
import (
"encoding/json"
"strings"
"github.com/go-openapi/jsonpointer"
"github.com/go-openapi/swag"
)
const (
jsonArray = "array"
)
// HeaderProps describes a response header
type HeaderProps struct {
Description string `json:"description,omitempty"`
}
// Header describes a header for a response of the API
//
// For more information: http://goo.gl/8us55a#headerObject
type Header struct {
CommonValidations
SimpleSchema
VendorExtensible
HeaderProps
}
// 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
func (h *Header) WithDescription(description string) *Header {
h.Description = description
return h
}
// 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
func (h *Header) CollectionOf(items *Items, format string) *Header {
h.Type = jsonArray
h.Items = items
h.CollectionFormat = format
return h
}
// WithDefault sets the default value on this item
func (h *Header) WithDefault(defaultValue interface{}) *Header {
h.Default = defaultValue
return h
}
// WithMaxLength sets a max length value
func (h *Header) WithMaxLength(max int64) *Header {
h.MaxLength = &max
return h
}
// WithMinLength sets a min length value
func (h *Header) WithMinLength(min int64) *Header {
h.MinLength = &min
return h
}
// WithPattern sets a pattern value
func (h *Header) WithPattern(pattern string) *Header {
h.Pattern = pattern
return h
}
// WithMultipleOf sets a multiple of value
func (h *Header) WithMultipleOf(number float64) *Header {
h.MultipleOf = &number
return h
}
// WithMaximum sets a maximum number value
func (h *Header) WithMaximum(max float64, exclusive bool) *Header {
h.Maximum = &max
h.ExclusiveMaximum = exclusive
return h
}
// WithMinimum sets a minimum number value
func (h *Header) WithMinimum(min float64, exclusive bool) *Header {
h.Minimum = &min
h.ExclusiveMinimum = exclusive
return h
}
// WithEnum sets a the enum values (replace)
func (h *Header) WithEnum(values ...interface{}) *Header {
h.Enum = append([]interface{}{}, values...)
return h
}
// WithMaxItems sets the max items
func (h *Header) WithMaxItems(size int64) *Header {
h.MaxItems = &size
return h
}
// 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
func (h *Header) UniqueValues() *Header {
h.UniqueItems = true
return h
}
// 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
func (h *Header) WithValidations(val CommonValidations) *Header {
h.SetValidations(SchemaValidations{CommonValidations: val})
return h
}
// MarshalJSON marshal this to JSON
func (h Header) MarshalJSON() ([]byte, error) {
b1, err := json.Marshal(h.CommonValidations)
if err != nil {
return nil, err
}
b2, err := json.Marshal(h.SimpleSchema)
if err != nil {
return nil, err
}
b3, err := json.Marshal(h.HeaderProps)
if err != nil {
return nil, err
}
return swag.ConcatJSON(b1, b2, b3), nil
}
// UnmarshalJSON unmarshals this header from JSON
func (h *Header) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &h.CommonValidations); err != nil {
return err
}
if err := json.Unmarshal(data, &h.SimpleSchema); err != nil {
return err
}
if err := json.Unmarshal(data, &h.VendorExtensible); err != nil {
return err
}
return json.Unmarshal(data, &h.HeaderProps)
}
// JSONLookup look up a value by the json property name
func (h Header) JSONLookup(token string) (interface{}, error) {
if ex, ok := h.Extensions[token]; ok {
return &ex, nil
}
r, _, err := jsonpointer.GetForToken(h.CommonValidations, token)
if err != nil && !strings.HasPrefix(err.Error(), "object has no field") {
return nil, err
}
if r != nil {
return r, nil
}
r, _, err = jsonpointer.GetForToken(h.SimpleSchema, token)
if err != nil && !strings.HasPrefix(err.Error(), "object has no field") {
return nil, err
}
if r != nil {
return r, nil
}
r, _, err = jsonpointer.GetForToken(h.HeaderProps, token)
return r, err
}

View file

@ -1,184 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package spec
import (
"encoding/json"
"strconv"
"strings"
"github.com/go-openapi/jsonpointer"
"github.com/go-openapi/swag"
)
// Extensions vendor specific extensions
type Extensions map[string]interface{}
// Add adds a value to these extensions
func (e Extensions) Add(key string, value interface{}) {
realKey := strings.ToLower(key)
e[realKey] = value
}
// 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)
return str, ok
}
return "", false
}
// GetInt gets a int value from the extensions
func (e Extensions) GetInt(key string) (int, bool) {
realKey := strings.ToLower(key)
if v, ok := e.GetString(realKey); ok {
if r, err := strconv.Atoi(v); err == nil {
return r, true
}
}
if v, ok := e[realKey]; ok {
if r, rOk := v.(float64); rOk {
return int(r), true
}
}
return -1, false
}
// 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)
return str, ok
}
return false, false
}
// GetStringSlice gets a string value from the extensions
func (e Extensions) GetStringSlice(key string) ([]string, bool) {
if v, ok := e[strings.ToLower(key)]; ok {
arr, isSlice := v.([]interface{})
if !isSlice {
return nil, false
}
var strs []string
for _, iface := range arr {
str, isString := iface.(string)
if !isString {
return nil, false
}
strs = append(strs, str)
}
return strs, ok
}
return nil, false
}
// VendorExtensible composition block.
type VendorExtensible struct {
Extensions Extensions
}
// AddExtension adds an extension to this extensible object
func (v *VendorExtensible) AddExtension(key string, value interface{}) {
if value == nil {
return
}
if v.Extensions == nil {
v.Extensions = make(map[string]interface{})
}
v.Extensions.Add(key, value)
}
// MarshalJSON marshals the extensions to json
func (v VendorExtensible) MarshalJSON() ([]byte, error) {
toser := make(map[string]interface{})
for k, v := range v.Extensions {
lk := strings.ToLower(k)
if strings.HasPrefix(lk, "x-") {
toser[k] = v
}
}
return json.Marshal(toser)
}
// UnmarshalJSON for this extensible object
func (v *VendorExtensible) UnmarshalJSON(data []byte) error {
var d map[string]interface{}
if err := json.Unmarshal(data, &d); err != nil {
return err
}
for k, vv := range d {
lk := strings.ToLower(k)
if strings.HasPrefix(lk, "x-") {
if v.Extensions == nil {
v.Extensions = map[string]interface{}{}
}
v.Extensions[k] = vv
}
}
return nil
}
// InfoProps the properties for an info definition
type InfoProps struct {
Description string `json:"description,omitempty"`
Title string `json:"title,omitempty"`
TermsOfService string `json:"termsOfService,omitempty"`
Contact *ContactInfo `json:"contact,omitempty"`
License *License `json:"license,omitempty"`
Version string `json:"version,omitempty"`
}
// Info object provides metadata about the API.
// The metadata can be used by the clients if needed, and can be presented in the Swagger-UI for convenience.
//
// For more information: http://goo.gl/8us55a#infoObject
type Info struct {
VendorExtensible
InfoProps
}
// JSONLookup look up a value by the json property name
func (i Info) JSONLookup(token string) (interface{}, error) {
if ex, ok := i.Extensions[token]; ok {
return &ex, nil
}
r, _, err := jsonpointer.GetForToken(i.InfoProps, token)
return r, err
}
// MarshalJSON marshal this to JSON
func (i Info) MarshalJSON() ([]byte, error) {
b1, err := json.Marshal(i.InfoProps)
if err != nil {
return nil, err
}
b2, err := json.Marshal(i.VendorExtensible)
if err != nil {
return nil, err
}
return swag.ConcatJSON(b1, b2), nil
}
// UnmarshalJSON marshal this from JSON
func (i *Info) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &i.InfoProps); err != nil {
return err
}
return json.Unmarshal(data, &i.VendorExtensible)
}

View file

@ -1,234 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package spec
import (
"encoding/json"
"strings"
"github.com/go-openapi/jsonpointer"
"github.com/go-openapi/swag"
)
const (
jsonRef = "$ref"
)
// SimpleSchema describe swagger simple schemas for parameters and headers
type SimpleSchema struct {
Type string `json:"type,omitempty"`
Nullable bool `json:"nullable,omitempty"`
Format string `json:"format,omitempty"`
Items *Items `json:"items,omitempty"`
CollectionFormat string `json:"collectionFormat,omitempty"`
Default interface{} `json:"default,omitempty"`
Example interface{} `json:"example,omitempty"`
}
// TypeName return the type (or format) of a simple schema
func (s *SimpleSchema) TypeName() string {
if s.Format != "" {
return s.Format
}
return s.Type
}
// ItemsTypeName yields the type of items in a simple schema array
func (s *SimpleSchema) ItemsTypeName() string {
if s.Items == nil {
return ""
}
return s.Items.TypeName()
}
// Items a limited subset of JSON-Schema's items object.
// It is used by parameter definitions that are not located in "body".
//
// For more information: http://goo.gl/8us55a#items-object
type Items struct {
Refable
CommonValidations
SimpleSchema
VendorExtensible
}
// NewItems creates a new instance of items
func NewItems() *Items {
return &Items{}
}
// Typed a fluent builder method for the type of item
func (i *Items) Typed(tpe, format string) *Items {
i.Type = tpe
i.Format = format
return i
}
// AsNullable flags this schema as nullable.
func (i *Items) AsNullable() *Items {
i.Nullable = true
return i
}
// CollectionOf a fluent builder method for an array item
func (i *Items) CollectionOf(items *Items, format string) *Items {
i.Type = jsonArray
i.Items = items
i.CollectionFormat = format
return i
}
// WithDefault sets the default value on this item
func (i *Items) WithDefault(defaultValue interface{}) *Items {
i.Default = defaultValue
return i
}
// WithMaxLength sets a max length value
func (i *Items) WithMaxLength(max int64) *Items {
i.MaxLength = &max
return i
}
// WithMinLength sets a min length value
func (i *Items) WithMinLength(min int64) *Items {
i.MinLength = &min
return i
}
// WithPattern sets a pattern value
func (i *Items) WithPattern(pattern string) *Items {
i.Pattern = pattern
return i
}
// WithMultipleOf sets a multiple of value
func (i *Items) WithMultipleOf(number float64) *Items {
i.MultipleOf = &number
return i
}
// WithMaximum sets a maximum number value
func (i *Items) WithMaximum(max float64, exclusive bool) *Items {
i.Maximum = &max
i.ExclusiveMaximum = exclusive
return i
}
// WithMinimum sets a minimum number value
func (i *Items) WithMinimum(min float64, exclusive bool) *Items {
i.Minimum = &min
i.ExclusiveMinimum = exclusive
return i
}
// WithEnum sets a the enum values (replace)
func (i *Items) WithEnum(values ...interface{}) *Items {
i.Enum = append([]interface{}{}, values...)
return i
}
// WithMaxItems sets the max items
func (i *Items) WithMaxItems(size int64) *Items {
i.MaxItems = &size
return i
}
// 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
func (i *Items) UniqueValues() *Items {
i.UniqueItems = true
return i
}
// 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
func (i *Items) WithValidations(val CommonValidations) *Items {
i.SetValidations(SchemaValidations{CommonValidations: val})
return i
}
// 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 {
return err
}
var ref Refable
if err := json.Unmarshal(data, &ref); err != nil {
return err
}
var simpleSchema SimpleSchema
if err := json.Unmarshal(data, &simpleSchema); err != nil {
return err
}
var vendorExtensible VendorExtensible
if err := json.Unmarshal(data, &vendorExtensible); err != nil {
return err
}
i.Refable = ref
i.CommonValidations = validations
i.SimpleSchema = simpleSchema
i.VendorExtensible = vendorExtensible
return nil
}
// MarshalJSON converts this items object to JSON
func (i Items) MarshalJSON() ([]byte, error) {
b1, err := json.Marshal(i.CommonValidations)
if err != nil {
return nil, err
}
b2, err := json.Marshal(i.SimpleSchema)
if err != nil {
return nil, err
}
b3, err := json.Marshal(i.Refable)
if err != nil {
return nil, err
}
b4, err := json.Marshal(i.VendorExtensible)
if err != nil {
return nil, err
}
return swag.ConcatJSON(b4, b3, b1, b2), nil
}
// JSONLookup look up a value by the json property name
func (i Items) JSONLookup(token string) (interface{}, error) {
if token == jsonRef {
return &i.Ref, nil
}
r, _, err := jsonpointer.GetForToken(i.CommonValidations, token)
if err != nil && !strings.HasPrefix(err.Error(), "object has no field") {
return nil, err
}
if r != nil {
return r, nil
}
r, _, err = jsonpointer.GetForToken(i.SimpleSchema, token)
return r, err
}

View file

@ -1,56 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package spec
import (
"encoding/json"
"github.com/go-openapi/swag"
)
// License information for the exposed API.
//
// For more information: http://goo.gl/8us55a#licenseObject
type License struct {
LicenseProps
VendorExtensible
}
// 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
func (l *License) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &l.LicenseProps); err != nil {
return err
}
return json.Unmarshal(data, &l.VendorExtensible)
}
// MarshalJSON produces License as json
func (l License) MarshalJSON() ([]byte, error) {
b1, err := json.Marshal(l.LicenseProps)
if err != nil {
return nil, err
}
b2, err := json.Marshal(l.VendorExtensible)
if err != nil {
return nil, err
}
return swag.ConcatJSON(b1, b2), nil
}

View file

@ -1,202 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package spec
import (
"net/url"
"path"
"strings"
)
const fileScheme = "file"
// normalizeURI ensures that all $ref paths used internally by the expander are canonicalized.
//
// NOTE(windows): there is a tolerance over the strict URI format on windows.
//
// The normalizer accepts relative file URLs like 'Path\File.JSON' as well as absolute file URLs like
// 'C:\Path\file.Yaml'.
//
// Both are canonicalized with a "file://" scheme, slashes and a lower-cased path:
// 'file:///c:/path/file.yaml'
//
// URLs can be specified with a file scheme, like in 'file:///folder/file.json' or
// 'file:///c:\folder\File.json'.
//
// URLs like file://C:\folder are considered invalid (i.e. there is no host 'c:\folder') and a "repair"
// is attempted.
//
// The base path argument is assumed to be canonicalized (e.g. using normalizeBase()).
func normalizeURI(refPath, base string) string {
refURL, err := parseURL(refPath)
if err != nil {
specLogger.Printf("warning: invalid URI in $ref %q: %v", refPath, err)
refURL, refPath = repairURI(refPath)
}
fixWindowsURI(refURL, refPath) // noop on non-windows OS
refURL.Path = path.Clean(refURL.Path)
if refURL.Path == "." {
refURL.Path = ""
}
r := MustCreateRef(refURL.String())
if r.IsCanonical() {
return refURL.String()
}
baseURL, _ := parseURL(base)
if path.IsAbs(refURL.Path) {
baseURL.Path = refURL.Path
} else if refURL.Path != "" {
baseURL.Path = path.Join(path.Dir(baseURL.Path), refURL.Path)
}
// copying fragment from ref to base
baseURL.Fragment = refURL.Fragment
return baseURL.String()
}
// denormalizeRef returns the simplest notation for a normalized $ref, given the path of the original root document.
//
// When calling this, we assume that:
// * $ref is a canonical URI
// * originalRelativeBase is a canonical URI
//
// denormalizeRef is currently used when we rewrite a $ref after a circular $ref has been detected.
// In this case, expansion stops and normally renders the internal canonical $ref.
//
// This internal $ref is eventually rebased to the original RelativeBase used for the expansion.
//
// There is a special case for schemas that are anchored with an "id":
// in that case, the rebasing is performed // against the id only if this is an anchor for the initial root document.
// All other intermediate "id"'s found along the way are ignored for the purpose of rebasing.
func denormalizeRef(ref *Ref, originalRelativeBase, id string) Ref {
debugLog("denormalizeRef called:\n$ref: %q\noriginal: %s\nroot ID:%s", ref.String(), originalRelativeBase, id)
if ref.String() == "" || ref.IsRoot() || ref.HasFragmentOnly {
// short circuit: $ref to current doc
return *ref
}
if id != "" {
idBaseURL, err := parseURL(id)
if err == nil { // if the schema id is not usable as a URI, ignore it
if ref, ok := rebase(ref, idBaseURL, true); ok { // rebase, but keep references to root unchaged (do not want $ref: "")
// $ref relative to the ID of the schema in the root document
return ref
}
}
}
originalRelativeBaseURL, _ := parseURL(originalRelativeBase)
r, _ := rebase(ref, originalRelativeBaseURL, false)
return r
}
func rebase(ref *Ref, v *url.URL, notEqual bool) (Ref, bool) {
var newBase url.URL
u := ref.GetURL()
if u.Scheme != v.Scheme || u.Host != v.Host {
return *ref, false
}
docPath := v.Path
v.Path = path.Dir(v.Path)
if v.Path == "." {
v.Path = ""
} else if !strings.HasSuffix(v.Path, "/") {
v.Path += "/"
}
newBase.Fragment = u.Fragment
if strings.HasPrefix(u.Path, docPath) {
newBase.Path = strings.TrimPrefix(u.Path, docPath)
} else {
newBase.Path = strings.TrimPrefix(u.Path, v.Path)
}
if notEqual && newBase.Path == "" && newBase.Fragment == "" {
// do not want rebasing to end up in an empty $ref
return *ref, false
}
if path.IsAbs(newBase.Path) {
// whenever we end up with an absolute path, specify the scheme and host
newBase.Scheme = v.Scheme
newBase.Host = v.Host
}
return MustCreateRef(newBase.String()), true
}
// 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
}
// normalizeBase performs a normalization of the input base path.
//
// This always yields a canonical URI (absolute), usable for the document cache.
//
// It ensures that all further internal work on basePath may safely assume
// a non-empty, cross-platform, canonical URI (i.e. absolute).
//
// This normalization tolerates windows paths (e.g. C:\x\y\File.dat) and transform this
// in a file:// URL with lower cased drive letter and path.
//
// See also: https://en.wikipedia.org/wiki/File_URI_scheme
func normalizeBase(in string) string {
u, err := parseURL(in)
if err != nil {
specLogger.Printf("warning: invalid URI in RelativeBase %q: %v", in, err)
u, in = repairURI(in)
}
u.Fragment = "" // any fragment in the base is irrelevant
fixWindowsURI(u, in) // noop on non-windows OS
u.Path = path.Clean(u.Path)
if u.Path == "." { // empty after Clean()
u.Path = ""
}
if u.Scheme != "" {
if path.IsAbs(u.Path) || u.Scheme != fileScheme {
// this is absolute or explicitly not a local file: we're good
return u.String()
}
}
// no scheme or file scheme with relative path: assume file and make it absolute
// enforce scheme file://... with absolute path.
//
// If the input path is relative, we anchor the path to the current working directory.
// NOTE: we may end up with a host component. Leave it unchanged: e.g. file://host/folder/file.json
u.Scheme = fileScheme
u.Path = absPath(u.Path) // platform-dependent
u.RawQuery = "" // any query component is irrelevant for a base
return u.String()
}

View file

@ -1,44 +0,0 @@
//go:build !windows
// +build !windows
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package spec
import (
"net/url"
"path/filepath"
)
// absPath makes a file path absolute and compatible with a URI path component.
//
// The parameter must be a path, not an URI.
func absPath(in string) string {
anchored, err := filepath.Abs(in)
if err != nil {
specLogger.Printf("warning: could not resolve current working directory: %v", err)
return in
}
return anchored
}
func repairURI(in string) (*url.URL, string) {
u, _ := parseURL("")
debugLog("repaired URI: original: %q, repaired: %q", in, "")
return u, ""
}
func fixWindowsURI(_ *url.URL, _ string) {
}

View file

@ -1,154 +0,0 @@
// -build windows
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package spec
import (
"net/url"
"os"
"path"
"path/filepath"
"strings"
)
// absPath makes a file path absolute and compatible with a URI path component
//
// The parameter must be a path, not an URI.
func absPath(in string) string {
// NOTE(windows): filepath.Abs exhibits a special behavior on windows for empty paths.
// See https://github.com/golang/go/issues/24441
if in == "" {
in = "."
}
anchored, err := filepath.Abs(in)
if err != nil {
specLogger.Printf("warning: could not resolve current working directory: %v", err)
return in
}
pth := strings.ReplaceAll(strings.ToLower(anchored), `\`, `/`)
if !strings.HasPrefix(pth, "/") {
pth = "/" + pth
}
return path.Clean(pth)
}
// repairURI tolerates invalid file URIs with common typos
// such as 'file://E:\folder\file', that break the regular URL parser.
//
// Adopting the same defaults as for unixes (e.g. return an empty path) would
// result into a counter-intuitive result for that case (e.g. E:\folder\file is
// eventually resolved as the current directory). The repair will detect the missing "/".
//
// Note that this only works for the file scheme.
func repairURI(in string) (*url.URL, string) {
const prefix = fileScheme + "://"
if !strings.HasPrefix(in, prefix) {
// giving up: resolve to empty path
u, _ := parseURL("")
return u, ""
}
// attempt the repair, stripping the scheme should be sufficient
u, _ := parseURL(strings.TrimPrefix(in, prefix))
debugLog("repaired URI: original: %q, repaired: %q", in, u.String())
return u, u.String()
}
// fixWindowsURI tolerates an absolute file path on windows such as C:\Base\File.yaml or \\host\share\Base\File.yaml
// and makes it a canonical URI: file:///c:/base/file.yaml
//
// Catch 22 notes for Windows:
//
// * There may be a drive letter on windows (it is lower-cased)
// * There may be a share UNC, e.g. \\server\folder\data.xml
// * Paths are case insensitive
// * Paths may already contain slashes
// * Paths must be slashed
//
// NOTE: there is no escaping. "/" may be valid separators just like "\".
// We don't use ToSlash() (which escapes everything) because windows now also
// tolerates the use of "/". Hence, both C:\File.yaml and C:/File.yaml will work.
func fixWindowsURI(u *url.URL, in string) {
drive := filepath.VolumeName(in)
if len(drive) > 0 {
if len(u.Scheme) == 1 && strings.EqualFold(u.Scheme, drive[:1]) { // a path with a drive letter
u.Scheme = fileScheme
u.Host = ""
u.Path = strings.Join([]string{drive, u.Opaque, u.Path}, `/`) // reconstruct the full path component (no fragment, no query)
} else if u.Host == "" && strings.HasPrefix(u.Path, drive) { // a path with a \\host volume
// NOTE: the special host@port syntax for UNC is not supported (yet)
u.Scheme = fileScheme
// this is a modified version of filepath.Dir() to apply on the VolumeName itself
i := len(drive) - 1
for i >= 0 && !os.IsPathSeparator(drive[i]) {
i--
}
host := drive[:i] // \\host\share => host
u.Path = strings.TrimPrefix(u.Path, host)
u.Host = strings.TrimPrefix(host, `\\`)
}
u.Opaque = ""
u.Path = strings.ReplaceAll(strings.ToLower(u.Path), `\`, `/`)
// ensure we form an absolute path
if !strings.HasPrefix(u.Path, "/") {
u.Path = "/" + u.Path
}
u.Path = path.Clean(u.Path)
return
}
if u.Scheme == fileScheme {
// Handle dodgy cases for file://{...} URIs on windows.
// A canonical URI should always be followed by an absolute path.
//
// Examples:
// * file:///folder/file => valid, unchanged
// * file:///c:\folder\file => slashed
// * file:///./folder/file => valid, cleaned to remove the dot
// * file:///.\folder\file => remapped to cwd
// * file:///. => dodgy, remapped to / (consistent with the behavior on unix)
// * file:///.. => dodgy, remapped to / (consistent with the behavior on unix)
if (!path.IsAbs(u.Path) && !filepath.IsAbs(u.Path)) || (strings.HasPrefix(u.Path, `/.`) && strings.Contains(u.Path, `\`)) {
// ensure we form an absolute path
u.Path, _ = filepath.Abs(strings.TrimLeft(u.Path, `/`))
if !strings.HasPrefix(u.Path, "/") {
u.Path = "/" + u.Path
}
}
u.Path = strings.ToLower(u.Path)
}
// NOTE: lower case normalization does not propagate to inner resources,
// generated when rebasing: when joining a relative URI with a file to an absolute base,
// only the base is currently lower-cased.
//
// For now, we assume this is good enough for most use cases
// and try not to generate too many differences
// between the output produced on different platforms.
u.Path = path.Clean(strings.ReplaceAll(u.Path, `\`, `/`))
}

View file

@ -1,400 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package spec
import (
"bytes"
"encoding/gob"
"encoding/json"
"sort"
"github.com/go-openapi/jsonpointer"
"github.com/go-openapi/swag"
)
func init() {
gob.Register(map[string]interface{}{})
gob.Register([]interface{}{})
}
// OperationProps describes an operation
//
// NOTES:
// - schemes, when present must be from [http, https, ws, wss]: see validate
// - Security is handled as a special case: see MarshalJSON function
type OperationProps struct {
Description string `json:"description,omitempty"`
Consumes []string `json:"consumes,omitempty"`
Produces []string `json:"produces,omitempty"`
Schemes []string `json:"schemes,omitempty"`
Tags []string `json:"tags,omitempty"`
Summary string `json:"summary,omitempty"`
ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"`
ID string `json:"operationId,omitempty"`
Deprecated bool `json:"deprecated,omitempty"`
Security []map[string][]string `json:"security,omitempty"`
Parameters []Parameter `json:"parameters,omitempty"`
Responses *Responses `json:"responses,omitempty"`
}
// MarshalJSON takes care of serializing operation properties to JSON
//
// We use a custom marhaller here to handle a special cases related to
// the Security field. We need to preserve zero length slice
// while omitting the field when the value is nil/unset.
func (op OperationProps) MarshalJSON() ([]byte, error) {
type Alias OperationProps
if op.Security == nil {
return json.Marshal(&struct {
Security []map[string][]string `json:"security,omitempty"`
*Alias
}{
Security: op.Security,
Alias: (*Alias)(&op),
})
}
return json.Marshal(&struct {
Security []map[string][]string `json:"security"`
*Alias
}{
Security: op.Security,
Alias: (*Alias)(&op),
})
}
// Operation describes a single API operation on a path.
//
// For more information: http://goo.gl/8us55a#operationObject
type Operation struct {
VendorExtensible
OperationProps
}
// SuccessResponse gets a success response model
func (o *Operation) SuccessResponse() (*Response, int, bool) {
if o.Responses == nil {
return nil, 0, false
}
responseCodes := make([]int, 0, len(o.Responses.StatusCodeResponses))
for k := range o.Responses.StatusCodeResponses {
if k >= 200 && k < 300 {
responseCodes = append(responseCodes, k)
}
}
if len(responseCodes) > 0 {
sort.Ints(responseCodes)
v := o.Responses.StatusCodeResponses[responseCodes[0]]
return &v, responseCodes[0], true
}
return o.Responses.Default, 0, false
}
// JSONLookup look up a value by the json property name
func (o Operation) JSONLookup(token string) (interface{}, error) {
if ex, ok := o.Extensions[token]; ok {
return &ex, nil
}
r, _, err := jsonpointer.GetForToken(o.OperationProps, token)
return r, err
}
// 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
}
return json.Unmarshal(data, &o.VendorExtensible)
}
// MarshalJSON converts this items object to JSON
func (o Operation) MarshalJSON() ([]byte, error) {
b1, err := json.Marshal(o.OperationProps)
if err != nil {
return nil, err
}
b2, err := json.Marshal(o.VendorExtensible)
if err != nil {
return nil, err
}
concated := swag.ConcatJSON(b1, b2)
return concated, nil
}
// NewOperation creates a new operation instance.
// It expects an ID as parameter but not passing an ID is also valid.
func NewOperation(id string) *Operation {
op := new(Operation)
op.ID = id
return op
}
// WithID sets the ID property on this operation, allows for chaining.
func (o *Operation) WithID(id string) *Operation {
o.ID = id
return o
}
// 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
func (o *Operation) WithSummary(summary string) *Operation {
o.Summary = summary
return o
}
// WithExternalDocs sets/removes the external docs for/from this operation.
// When you pass empty strings as params the external documents will be removed.
// When you pass non-empty string as one value then those values will be used on the external docs object.
// So when you pass a non-empty description, you should also pass the url and vice versa.
func (o *Operation) WithExternalDocs(description, url string) *Operation {
if description == "" && url == "" {
o.ExternalDocs = nil
return o
}
if o.ExternalDocs == nil {
o.ExternalDocs = &ExternalDocumentation{}
}
o.ExternalDocs.Description = description
o.ExternalDocs.URL = url
return o
}
// Deprecate marks the operation as deprecated
func (o *Operation) Deprecate() *Operation {
o.Deprecated = true
return o
}
// Undeprecate marks the operation as not deprected
func (o *Operation) Undeprecate() *Operation {
o.Deprecated = false
return o
}
// 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
func (o *Operation) WithProduces(mediaTypes ...string) *Operation {
o.Produces = append(o.Produces, mediaTypes...)
return o
}
// 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
func (o *Operation) AddParam(param *Parameter) *Operation {
if param == nil {
return o
}
for i, p := range o.Parameters {
if p.Name == param.Name && p.In == param.In {
params := make([]Parameter, 0, len(o.Parameters)+1)
params = append(params, o.Parameters[:i]...)
params = append(params, *param)
params = append(params, o.Parameters[i+1:]...)
o.Parameters = params
return o
}
}
o.Parameters = append(o.Parameters, *param)
return o
}
// 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 {
o.Parameters = append(o.Parameters[:i], o.Parameters[i+1:]...)
return o
}
}
return o
}
// SecuredWith adds a security scope to this operation.
func (o *Operation) SecuredWith(name string, scopes ...string) *Operation {
o.Security = append(o.Security, map[string][]string{name: scopes})
return o
}
// WithDefaultResponse adds a default response to the operation.
// 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
func (o *Operation) RespondsWith(code int, response *Response) *Operation {
if o.Responses == nil {
o.Responses = new(Responses)
}
if code == 0 {
o.Responses.Default = response
return o
}
if response == nil {
delete(o.Responses.StatusCodeResponses, code)
return o
}
if o.Responses.StatusCodeResponses == nil {
o.Responses.StatusCodeResponses = make(map[int]Response)
}
o.Responses.StatusCodeResponses[code] = *response
return o
}
type opsAlias OperationProps
type gobAlias struct {
Security []map[string]struct {
List []string
Pad bool
}
Alias *opsAlias
SecurityIsEmpty bool
}
// GobEncode provides a safe gob encoder for Operation, including empty security requirements
func (o Operation) GobEncode() ([]byte, error) {
raw := struct {
Ext VendorExtensible
Props OperationProps
}{
Ext: o.VendorExtensible,
Props: o.OperationProps,
}
var b bytes.Buffer
err := gob.NewEncoder(&b).Encode(raw)
return b.Bytes(), err
}
// GobDecode provides a safe gob decoder for Operation, including empty security requirements
func (o *Operation) GobDecode(b []byte) error {
var raw struct {
Ext VendorExtensible
Props OperationProps
}
buf := bytes.NewBuffer(b)
err := gob.NewDecoder(buf).Decode(&raw)
if err != nil {
return err
}
o.VendorExtensible = raw.Ext
o.OperationProps = raw.Props
return nil
}
// GobEncode provides a safe gob encoder for Operation, including empty security requirements
func (op OperationProps) GobEncode() ([]byte, error) {
raw := gobAlias{
Alias: (*opsAlias)(&op),
}
var b bytes.Buffer
if op.Security == nil {
// nil security requirement
err := gob.NewEncoder(&b).Encode(raw)
return b.Bytes(), err
}
if len(op.Security) == 0 {
// empty, but non-nil security requirement
raw.SecurityIsEmpty = true
raw.Alias.Security = nil
err := gob.NewEncoder(&b).Encode(raw)
return b.Bytes(), err
}
raw.Security = make([]map[string]struct {
List []string
Pad bool
}, 0, len(op.Security))
for _, req := range op.Security {
v := make(map[string]struct {
List []string
Pad bool
}, len(req))
for k, val := range req {
v[k] = struct {
List []string
Pad bool
}{
List: val,
}
}
raw.Security = append(raw.Security, v)
}
err := gob.NewEncoder(&b).Encode(raw)
return b.Bytes(), err
}
// GobDecode provides a safe gob decoder for Operation, including empty security requirements
func (op *OperationProps) GobDecode(b []byte) error {
var raw gobAlias
buf := bytes.NewBuffer(b)
err := gob.NewDecoder(buf).Decode(&raw)
if err != nil {
return err
}
if raw.Alias == nil {
return nil
}
switch {
case raw.SecurityIsEmpty:
// empty, but non-nil security requirement
raw.Alias.Security = []map[string][]string{}
case len(raw.Alias.Security) == 0:
// nil security requirement
raw.Alias.Security = nil
default:
raw.Alias.Security = make([]map[string][]string, 0, len(raw.Security))
for _, req := range raw.Security {
v := make(map[string][]string, len(req))
for k, val := range req {
v[k] = make([]string, 0, len(val.List))
v[k] = append(v[k], val.List...)
}
raw.Alias.Security = append(raw.Alias.Security, v)
}
}
*op = *(*OperationProps)(raw.Alias)
return nil
}

Some files were not shown because too many files have changed in this diff Show more