New upstream release.
Debian Janitor
1 year, 2 months ago
0 | ## Contribution Guidelines | |
1 | ||
2 | ### Pull requests are always welcome | |
3 | ||
4 | We are always thrilled to receive pull requests, and do our best to | |
5 | process them as fast as possible. Not sure if that typo is worth a pull | |
6 | request? Do it! We will appreciate it. | |
7 | ||
8 | If your pull request is not accepted on the first try, don't be | |
9 | discouraged! If there's a problem with the implementation, hopefully you | |
10 | received feedback on what to improve. | |
11 | ||
12 | We're trying very hard to keep go-swagger lean and focused. We don't want it | |
13 | to do everything for everybody. This means that we might decide against | |
14 | incorporating a new feature. However, there might be a way to implement | |
15 | that feature *on top of* go-swagger. | |
16 | ||
17 | ||
18 | ### Conventions | |
19 | ||
20 | Fork the repo and make changes on your fork in a feature branch: | |
21 | ||
22 | - If it's a bugfix branch, name it XXX-something where XXX is the number of the | |
23 | issue | |
24 | - If it's a feature branch, create an enhancement issue to announce your | |
25 | intentions, and name it XXX-something where XXX is the number of the issue. | |
26 | ||
27 | Submit unit tests for your changes. Go has a great test framework built in; use | |
28 | it! Take a look at existing tests for inspiration. Run the full test suite on | |
29 | your branch before submitting a pull request. | |
30 | ||
31 | Update the documentation when creating or modifying features. Test | |
32 | your documentation changes for clarity, concision, and correctness, as | |
33 | well as a clean documentation build. See ``docs/README.md`` for more | |
34 | information on building the docs and how docs get released. | |
35 | ||
36 | Write clean code. Universally formatted code promotes ease of writing, reading, | |
37 | and maintenance. Always run `gofmt -s -w file.go` on each changed file before | |
38 | committing your changes. Most editors have plugins that do this automatically. | |
39 | ||
40 | Pull requests descriptions should be as clear as possible and include a | |
41 | reference to all the issues that they address. | |
42 | ||
43 | Pull requests must not contain commits from other users or branches. | |
44 | ||
45 | Commit messages must start with a capitalized and short summary (max. 50 | |
46 | chars) written in the imperative, followed by an optional, more detailed | |
47 | explanatory text which is separated from the summary by an empty line. | |
48 | ||
49 | Code review comments may be added to your pull request. Discuss, then make the | |
50 | suggested modifications and push additional commits to your feature branch. Be | |
51 | sure to post a comment after pushing. The new commits will show up in the pull | |
52 | request automatically, but the reviewers will not be notified unless you | |
53 | comment. | |
54 | ||
55 | Before the pull request is merged, make sure that you squash your commits into | |
56 | logical units of work using `git rebase -i` and `git push -f`. After every | |
57 | commit the test suite should be passing. Include documentation changes in the | |
58 | same commit so that a revert would remove all traces of the feature or fix. | |
59 | ||
60 | Commits that fix or close an issue should include a reference like `Closes #XXX` | |
61 | or `Fixes #XXX`, which will automatically close the issue when merged. | |
62 | ||
63 | ### Sign your work | |
64 | ||
65 | The sign-off is a simple line at the end of the explanation for the | |
66 | patch, which certifies that you wrote it or otherwise have the right to | |
67 | pass it on as an open-source patch. The rules are pretty simple: if you | |
68 | can certify the below (from | |
69 | [developercertificate.org](http://developercertificate.org/)): | |
70 | ||
71 | ``` | |
72 | Developer Certificate of Origin | |
73 | Version 1.1 | |
74 | ||
75 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. | |
76 | 660 York Street, Suite 102, | |
77 | San Francisco, CA 94110 USA | |
78 | ||
79 | Everyone is permitted to copy and distribute verbatim copies of this | |
80 | license document, but changing it is not allowed. | |
81 | ||
82 | ||
83 | Developer's Certificate of Origin 1.1 | |
84 | ||
85 | By making a contribution to this project, I certify that: | |
86 | ||
87 | (a) The contribution was created in whole or in part by me and I | |
88 | have the right to submit it under the open source license | |
89 | indicated in the file; or | |
90 | ||
91 | (b) The contribution is based upon previous work that, to the best | |
92 | of my knowledge, is covered under an appropriate open source | |
93 | license and I have the right under that license to submit that | |
94 | work with modifications, whether created in whole or in part | |
95 | by me, under the same open source license (unless I am | |
96 | permitted to submit under a different license), as indicated | |
97 | in the file; or | |
98 | ||
99 | (c) The contribution was provided directly to me by some other | |
100 | person who certified (a), (b) or (c) and I have not modified | |
101 | it. | |
102 | ||
103 | (d) I understand and agree that this project and the contribution | |
104 | are public and that a record of the contribution (including all | |
105 | personal information I submit with it, including my sign-off) is | |
106 | maintained indefinitely and may be redistributed consistent with | |
107 | this project or the open source license(s) involved. | |
108 | ``` | |
109 | ||
110 | then you just add a line to every git commit message: | |
111 | ||
112 | Signed-off-by: Joe Smith <joe@gmail.com> | |
113 | ||
114 | using your real name (sorry, no pseudonyms or anonymous contributions.) | |
115 | ||
116 | You can add the sign off when creating the git commit via `git commit -s`. |
0 | name: Go | |
1 | ||
2 | on: [push, pull_request] | |
3 | ||
4 | jobs: | |
5 | build: | |
6 | runs-on: ${{ matrix.os }} | |
7 | ||
8 | strategy: | |
9 | matrix: | |
10 | # No Windows this time. Some tests expect Unix-style paths. | |
11 | os: [ ubuntu-latest, macos-latest ] | |
12 | fail-fast: false | |
13 | ||
14 | steps: | |
15 | - uses: actions/checkout@v2 | |
16 | ||
17 | - name: Set up Go | |
18 | uses: actions/setup-go@v2 | |
19 | with: | |
20 | go-version: 1.17 | |
21 | ||
22 | - name: Setup gotestsum | |
23 | uses: autero1/action-gotestsum@v1.0.0 | |
24 | with: | |
25 | gotestsum_version: 1.7.0 | |
26 | ||
27 | - name: Test | |
28 | run: gotestsum --format short-verbose -- -race -timeout=20m -coverprofile=coverage_txt -covermode=atomic ./... | |
29 | ||
30 | - uses: codecov/codecov-action@v2 | |
31 | with: | |
32 | files: coverage_txt | |
33 | ||
34 | lint: | |
35 | runs-on: ${{ matrix.os }} | |
36 | ||
37 | strategy: | |
38 | matrix: | |
39 | os: [ ubuntu-latest, macos-latest, windows-latest ] | |
40 | fail-fast: false | |
41 | ||
42 | steps: | |
43 | - uses: actions/checkout@v2 | |
44 | - uses: golangci/golangci-lint-action@v2 | |
45 | with: | |
46 | args: --timeout=5m |
47 | 47 | - goimports |
48 | 48 | - tenv |
49 | 49 | - golint |
50 | - exhaustruct | |
51 | - nilnil | |
52 | - nonamedreturns | |
53 | - nosnakecase |
0 | golang-github-go-openapi-swag (1:0.22.3-1) UNRELEASED; urgency=low | |
1 | ||
2 | * New upstream release. | |
3 | ||
4 | -- Debian Janitor <janitor@jelmer.uk> Wed, 08 Feb 2023 17:53:12 -0000 | |
5 | ||
0 | 6 | golang-github-go-openapi-swag (1:0.21.1-1) unstable; urgency=medium |
1 | 7 | |
2 | 8 | * Team upload. |
16 | 16 | |
17 | 17 | You may also use it standalone for your projects. |
18 | 18 | |
19 | * convert between value and pointers for builtin types | |
20 | * convert from string to builtin types (wraps strconv) | |
21 | * fast json concatenation | |
22 | * search in path | |
23 | * load from file or http | |
24 | * name mangling | |
25 | ||
19 | - convert between value and pointers for builtin types | |
20 | - convert from string to builtin types (wraps strconv) | |
21 | - fast json concatenation | |
22 | - search in path | |
23 | - load from file or http | |
24 | - name mangling | |
26 | 25 | |
27 | 26 | This repo has only few dependencies outside of the standard library: |
28 | 27 | |
29 | * YAML utilities depend on gopkg.in/yaml.v2 | |
28 | - YAML utilities depend on gopkg.in/yaml.v2 | |
30 | 29 | */ |
31 | 30 | package swag |
0 | 0 | module github.com/go-openapi/swag |
1 | 1 | |
2 | 2 | require ( |
3 | github.com/davecgh/go-spew v1.1.1 // indirect | |
4 | github.com/kr/text v0.2.0 // indirect | |
5 | github.com/mailru/easyjson v0.7.6 | |
6 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect | |
7 | github.com/stretchr/testify v1.6.1 | |
8 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect | |
9 | gopkg.in/yaml.v2 v2.4.0 | |
10 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect | |
3 | github.com/mailru/easyjson v0.7.7 | |
4 | github.com/stretchr/testify v1.8.0 | |
5 | gopkg.in/yaml.v3 v3.0.1 | |
11 | 6 | ) |
12 | 7 | |
13 | replace github.com/golang/lint => golang.org/x/lint v0.0.0-20190409202823-959b441ac422 | |
8 | require ( | |
9 | github.com/davecgh/go-spew v1.1.1 // indirect | |
10 | github.com/josharian/intern v1.0.0 // indirect | |
11 | github.com/kr/text v0.2.0 // indirect | |
12 | github.com/pmezard/go-difflib v1.0.0 // indirect | |
13 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect | |
14 | ) | |
14 | 15 | |
15 | replace sourcegraph.com/sourcegraph/go-diff => github.com/sourcegraph/go-diff v0.5.1 | |
16 | // replace github.com/golang/lint => golang.org/x/lint v0.0.0-20190409202823-959b441ac422 | |
16 | 17 | |
17 | go 1.11 | |
18 | // replace sourcegraph.com/sourcegraph/go-diff => github.com/sourcegraph/go-diff v0.5.1 | |
19 | ||
20 | go 1.18 |
3 | 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
4 | 4 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= |
5 | 5 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= |
6 | github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= | |
7 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= | |
6 | 8 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= |
7 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= | |
8 | 9 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= |
9 | 10 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= |
10 | 11 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= |
11 | github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= | |
12 | github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= | |
13 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= | |
14 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= | |
12 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= | |
13 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= | |
15 | 14 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= |
16 | 15 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
17 | github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= | |
18 | 16 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
19 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= | |
20 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | |
17 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | |
18 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | |
19 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= | |
20 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | |
21 | 21 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
22 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= | |
23 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | |
24 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= | |
25 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | |
22 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= | |
23 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= | |
26 | 24 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
27 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= | |
28 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | |
25 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | |
26 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
15 | 15 | |
16 | 16 | import ( |
17 | 17 | "fmt" |
18 | "io/ioutil" | |
18 | "io" | |
19 | 19 | "log" |
20 | 20 | "net/http" |
21 | 21 | "net/url" |
22 | "os" | |
22 | 23 | "path/filepath" |
23 | 24 | "runtime" |
24 | 25 | "strings" |
39 | 40 | |
40 | 41 | // LoadFromFileOrHTTP loads the bytes from a file or a remote http server based on the path passed in |
41 | 42 | func LoadFromFileOrHTTP(path string) ([]byte, error) { |
42 | return LoadStrategy(path, ioutil.ReadFile, loadHTTPBytes(LoadHTTPTimeout))(path) | |
43 | return LoadStrategy(path, os.ReadFile, loadHTTPBytes(LoadHTTPTimeout))(path) | |
43 | 44 | } |
44 | 45 | |
45 | 46 | // LoadFromFileOrHTTPWithTimeout loads the bytes from a file or a remote http server based on the path passed in |
46 | 47 | // timeout arg allows for per request overriding of the request timeout |
47 | 48 | func LoadFromFileOrHTTPWithTimeout(path string, timeout time.Duration) ([]byte, error) { |
48 | return LoadStrategy(path, ioutil.ReadFile, loadHTTPBytes(timeout))(path) | |
49 | return LoadStrategy(path, os.ReadFile, loadHTTPBytes(timeout))(path) | |
49 | 50 | } |
50 | 51 | |
51 | 52 | // LoadStrategy returns a loader function for a given path or uri |
85 | 86 | func loadHTTPBytes(timeout time.Duration) func(path string) ([]byte, error) { |
86 | 87 | return func(path string) ([]byte, error) { |
87 | 88 | client := &http.Client{Timeout: timeout} |
88 | req, err := http.NewRequest("GET", path, nil) // nolint: noctx | |
89 | req, err := http.NewRequest(http.MethodGet, path, nil) //nolint:noctx | |
89 | 90 | if err != nil { |
90 | 91 | return nil, err |
91 | 92 | } |
114 | 115 | return nil, fmt.Errorf("could not access document at %q [%s] ", path, resp.Status) |
115 | 116 | } |
116 | 117 | |
117 | return ioutil.ReadAll(resp.Body) | |
118 | return io.ReadAll(resp.Body) | |
118 | 119 | } |
119 | 120 | } |
14 | 14 | package swag |
15 | 15 | |
16 | 16 | import ( |
17 | "io/ioutil" | |
18 | 17 | "os" |
19 | 18 | "path" |
20 | 19 | "path/filepath" |
28 | 27 | if tgt == "" { |
29 | 28 | tgt = "pkgpaths" |
30 | 29 | } |
31 | td, err := ioutil.TempDir("", tgt) | |
30 | td, err := os.MkdirTemp("", tgt) | |
32 | 31 | if err != nil { |
33 | 32 | return "", "", err |
34 | 33 | } |
35 | td2, err := ioutil.TempDir("", tgt+"-2") | |
34 | td2, err := os.MkdirTemp("", tgt+"-2") | |
36 | 35 | if err != nil { |
37 | 36 | return "", "", err |
38 | 37 | } |
96 | 95 | assert.Empty(t, pkg) |
97 | 96 | } |
98 | 97 | |
99 | // nolint: unparam | |
98 | //nolint:unparam | |
100 | 99 | func assertPath(t testing.TB, expected, actual string) bool { |
101 | 100 | fp, err := filepath.EvalSymlinks(expected) |
102 | 101 | if assert.NoError(t, err) { |
98 | 98 | ) |
99 | 99 | |
100 | 100 | // JoinByFormat joins a string array by a known format (e.g. swagger's collectionFormat attribute): |
101 | // ssv: space separated value | |
102 | // tsv: tab separated value | |
103 | // pipes: pipe (|) separated value | |
104 | // csv: comma separated value (default) | |
101 | // | |
102 | // ssv: space separated value | |
103 | // tsv: tab separated value | |
104 | // pipes: pipe (|) separated value | |
105 | // csv: comma separated value (default) | |
105 | 106 | func JoinByFormat(data []string, format string) []string { |
106 | 107 | if len(data) == 0 { |
107 | 108 | return data |
123 | 124 | } |
124 | 125 | |
125 | 126 | // SplitByFormat splits a string by a known format: |
126 | // ssv: space separated value | |
127 | // tsv: tab separated value | |
128 | // pipes: pipe (|) separated value | |
129 | // csv: comma separated value (default) | |
130 | // | |
127 | // | |
128 | // ssv: space separated value | |
129 | // tsv: tab separated value | |
130 | // pipes: pipe (|) separated value | |
131 | // csv: comma separated value (default) | |
131 | 132 | func SplitByFormat(data, format string) []string { |
132 | 133 | if data == "" { |
133 | 134 | return nil |
21 | 21 | |
22 | 22 | "github.com/mailru/easyjson/jlexer" |
23 | 23 | "github.com/mailru/easyjson/jwriter" |
24 | yaml "gopkg.in/yaml.v2" | |
24 | yaml "gopkg.in/yaml.v3" | |
25 | 25 | ) |
26 | 26 | |
27 | 27 | // YAMLMatcher matches yaml |
42 | 42 | |
43 | 43 | // BytesToYAMLDoc converts a byte slice into a YAML document |
44 | 44 | func BytesToYAMLDoc(data []byte) (interface{}, error) { |
45 | var canary map[interface{}]interface{} // validate this is an object and not a different type | |
46 | if err := yaml.Unmarshal(data, &canary); err != nil { | |
47 | return nil, err | |
48 | } | |
49 | ||
50 | var document yaml.MapSlice // preserve order that is present in the document | |
45 | var document yaml.Node // preserve order that is present in the document | |
51 | 46 | if err := yaml.Unmarshal(data, &document); err != nil { |
52 | 47 | return nil, err |
53 | 48 | } |
54 | return document, nil | |
49 | if document.Kind != yaml.DocumentNode || len(document.Content) != 1 || document.Content[0].Kind != yaml.MappingNode { | |
50 | return nil, fmt.Errorf("only YAML documents that are objects are supported") | |
51 | } | |
52 | return &document, nil | |
53 | } | |
54 | ||
55 | func yamlNode(root *yaml.Node) (interface{}, error) { | |
56 | switch root.Kind { | |
57 | case yaml.DocumentNode: | |
58 | return yamlDocument(root) | |
59 | case yaml.SequenceNode: | |
60 | return yamlSequence(root) | |
61 | case yaml.MappingNode: | |
62 | return yamlMapping(root) | |
63 | case yaml.ScalarNode: | |
64 | return yamlScalar(root) | |
65 | case yaml.AliasNode: | |
66 | return yamlNode(root.Alias) | |
67 | default: | |
68 | return nil, fmt.Errorf("unsupported YAML node type: %v", root.Kind) | |
69 | } | |
70 | } | |
71 | ||
72 | func yamlDocument(node *yaml.Node) (interface{}, error) { | |
73 | if len(node.Content) != 1 { | |
74 | return nil, fmt.Errorf("unexpected YAML Document node content length: %d", len(node.Content)) | |
75 | } | |
76 | return yamlNode(node.Content[0]) | |
77 | } | |
78 | ||
79 | func yamlMapping(node *yaml.Node) (interface{}, error) { | |
80 | m := make(JSONMapSlice, len(node.Content)/2) | |
81 | ||
82 | var j int | |
83 | for i := 0; i < len(node.Content); i += 2 { | |
84 | var nmi JSONMapItem | |
85 | k, err := yamlStringScalarC(node.Content[i]) | |
86 | if err != nil { | |
87 | return nil, fmt.Errorf("unable to decode YAML map key: %w", err) | |
88 | } | |
89 | nmi.Key = k | |
90 | v, err := yamlNode(node.Content[i+1]) | |
91 | if err != nil { | |
92 | return nil, fmt.Errorf("unable to process YAML map value for key %q: %w", k, err) | |
93 | } | |
94 | nmi.Value = v | |
95 | m[j] = nmi | |
96 | j++ | |
97 | } | |
98 | return m, nil | |
99 | } | |
100 | ||
101 | func yamlSequence(node *yaml.Node) (interface{}, error) { | |
102 | s := make([]interface{}, 0) | |
103 | ||
104 | for i := 0; i < len(node.Content); i++ { | |
105 | ||
106 | v, err := yamlNode(node.Content[i]) | |
107 | if err != nil { | |
108 | return nil, fmt.Errorf("unable to decode YAML sequence value: %w", err) | |
109 | } | |
110 | s = append(s, v) | |
111 | } | |
112 | return s, nil | |
113 | } | |
114 | ||
115 | const ( // See https://yaml.org/type/ | |
116 | yamlStringScalar = "tag:yaml.org,2002:str" | |
117 | yamlIntScalar = "tag:yaml.org,2002:int" | |
118 | yamlBoolScalar = "tag:yaml.org,2002:bool" | |
119 | yamlFloatScalar = "tag:yaml.org,2002:float" | |
120 | yamlTimestamp = "tag:yaml.org,2002:timestamp" | |
121 | yamlNull = "tag:yaml.org,2002:null" | |
122 | ) | |
123 | ||
124 | func yamlScalar(node *yaml.Node) (interface{}, error) { | |
125 | switch node.LongTag() { | |
126 | case yamlStringScalar: | |
127 | return node.Value, nil | |
128 | case yamlBoolScalar: | |
129 | b, err := strconv.ParseBool(node.Value) | |
130 | if err != nil { | |
131 | return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting bool content: %w", node.Value, err) | |
132 | } | |
133 | return b, nil | |
134 | case yamlIntScalar: | |
135 | i, err := strconv.ParseInt(node.Value, 10, 64) | |
136 | if err != nil { | |
137 | return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting integer content: %w", node.Value, err) | |
138 | } | |
139 | return i, nil | |
140 | case yamlFloatScalar: | |
141 | f, err := strconv.ParseFloat(node.Value, 64) | |
142 | if err != nil { | |
143 | return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting float content: %w", node.Value, err) | |
144 | } | |
145 | return f, nil | |
146 | case yamlTimestamp: | |
147 | return node.Value, nil | |
148 | case yamlNull: | |
149 | return nil, nil | |
150 | default: | |
151 | return nil, fmt.Errorf("YAML tag %q is not supported", node.LongTag()) | |
152 | } | |
153 | } | |
154 | ||
155 | func yamlStringScalarC(node *yaml.Node) (string, error) { | |
156 | if node.Kind != yaml.ScalarNode { | |
157 | return "", fmt.Errorf("expecting a string scalar but got %q", node.Kind) | |
158 | } | |
159 | switch node.LongTag() { | |
160 | case yamlStringScalar, yamlIntScalar, yamlFloatScalar: | |
161 | return node.Value, nil | |
162 | default: | |
163 | return "", fmt.Errorf("YAML tag %q is not supported as map key", node.LongTag()) | |
164 | } | |
55 | 165 | } |
56 | 166 | |
57 | 167 | // JSONMapSlice represent a JSON object, with the order of keys maintained |
102 | 212 | result = append(result, mi) |
103 | 213 | } |
104 | 214 | *s = result |
215 | } | |
216 | ||
217 | func (s JSONMapSlice) MarshalYAML() (interface{}, error) { | |
218 | var n yaml.Node | |
219 | n.Kind = yaml.DocumentNode | |
220 | var nodes []*yaml.Node | |
221 | for _, item := range s { | |
222 | nn, err := json2yaml(item.Value) | |
223 | if err != nil { | |
224 | return nil, err | |
225 | } | |
226 | ns := []*yaml.Node{ | |
227 | { | |
228 | Kind: yaml.ScalarNode, | |
229 | Tag: yamlStringScalar, | |
230 | Value: item.Key, | |
231 | }, | |
232 | nn, | |
233 | } | |
234 | nodes = append(nodes, ns...) | |
235 | } | |
236 | ||
237 | n.Content = []*yaml.Node{ | |
238 | { | |
239 | Kind: yaml.MappingNode, | |
240 | Content: nodes, | |
241 | }, | |
242 | } | |
243 | ||
244 | return yaml.Marshal(&n) | |
245 | } | |
246 | ||
247 | func json2yaml(item interface{}) (*yaml.Node, error) { | |
248 | switch val := item.(type) { | |
249 | case JSONMapSlice: | |
250 | var n yaml.Node | |
251 | n.Kind = yaml.MappingNode | |
252 | for i := range val { | |
253 | childNode, err := json2yaml(&val[i].Value) | |
254 | if err != nil { | |
255 | return nil, err | |
256 | } | |
257 | n.Content = append(n.Content, &yaml.Node{ | |
258 | Kind: yaml.ScalarNode, | |
259 | Tag: yamlStringScalar, | |
260 | Value: val[i].Key, | |
261 | }, childNode) | |
262 | } | |
263 | return &n, nil | |
264 | case map[string]interface{}: | |
265 | var n yaml.Node | |
266 | n.Kind = yaml.MappingNode | |
267 | for k, v := range val { | |
268 | childNode, err := json2yaml(v) | |
269 | if err != nil { | |
270 | return nil, err | |
271 | } | |
272 | n.Content = append(n.Content, &yaml.Node{ | |
273 | Kind: yaml.ScalarNode, | |
274 | Tag: yamlStringScalar, | |
275 | Value: k, | |
276 | }, childNode) | |
277 | } | |
278 | return &n, nil | |
279 | case []interface{}: | |
280 | var n yaml.Node | |
281 | n.Kind = yaml.SequenceNode | |
282 | for i := range val { | |
283 | childNode, err := json2yaml(val[i]) | |
284 | if err != nil { | |
285 | return nil, err | |
286 | } | |
287 | n.Content = append(n.Content, childNode) | |
288 | } | |
289 | return &n, nil | |
290 | case string: | |
291 | return &yaml.Node{ | |
292 | Kind: yaml.ScalarNode, | |
293 | Tag: yamlStringScalar, | |
294 | Value: val, | |
295 | }, nil | |
296 | case float64: | |
297 | return &yaml.Node{ | |
298 | Kind: yaml.ScalarNode, | |
299 | Tag: yamlFloatScalar, | |
300 | Value: strconv.FormatFloat(val, 'f', -1, 64), | |
301 | }, nil | |
302 | case int64: | |
303 | return &yaml.Node{ | |
304 | Kind: yaml.ScalarNode, | |
305 | Tag: yamlIntScalar, | |
306 | Value: strconv.FormatInt(val, 10), | |
307 | }, nil | |
308 | case uint64: | |
309 | return &yaml.Node{ | |
310 | Kind: yaml.ScalarNode, | |
311 | Tag: yamlIntScalar, | |
312 | Value: strconv.FormatUint(val, 10), | |
313 | }, nil | |
314 | case bool: | |
315 | return &yaml.Node{ | |
316 | Kind: yaml.ScalarNode, | |
317 | Tag: yamlBoolScalar, | |
318 | Value: strconv.FormatBool(val), | |
319 | }, nil | |
320 | } | |
321 | return nil, nil | |
105 | 322 | } |
106 | 323 | |
107 | 324 | // JSONMapItem represents the value of a key in a JSON object held by JSONMapSlice |
172 | 389 | } |
173 | 390 | |
174 | 391 | switch in := input.(type) { |
175 | case yaml.MapSlice: | |
176 | ||
177 | o := make(JSONMapSlice, len(in)) | |
178 | for i, mi := range in { | |
179 | var nmi JSONMapItem | |
180 | if nmi.Key, err = format(mi.Key); err != nil { | |
181 | return nil, err | |
182 | } | |
183 | ||
184 | v, ert := transformData(mi.Value) | |
185 | if ert != nil { | |
186 | return nil, ert | |
187 | } | |
188 | nmi.Value = v | |
189 | o[i] = nmi | |
190 | } | |
191 | return o, nil | |
392 | case yaml.Node: | |
393 | return yamlNode(&in) | |
394 | case *yaml.Node: | |
395 | return yamlNode(in) | |
192 | 396 | case map[interface{}]interface{}: |
193 | 397 | o := make(JSONMapSlice, 0, len(in)) |
194 | 398 | for ke, va := range in { |
19 | 19 | "testing" |
20 | 20 | |
21 | 21 | "github.com/stretchr/testify/assert" |
22 | yaml "gopkg.in/yaml.v2" | |
22 | "github.com/stretchr/testify/require" | |
23 | yaml "gopkg.in/yaml.v3" | |
23 | 24 | ) |
24 | 25 | |
25 | /* currently unused: | |
26 | type failJSONMarshal struct { | |
26 | func TestJSONToYAML(t *testing.T) { | |
27 | sd := `{"1":"the int key value","name":"a string value","y":"some value"}` | |
28 | var data JSONMapSlice | |
29 | require.NoError(t, json.Unmarshal([]byte(sd), &data)) | |
30 | ||
31 | y, err := data.MarshalYAML() | |
32 | require.NoError(t, err) | |
33 | const expected = `"1": the int key value | |
34 | name: a string value | |
35 | y: some value | |
36 | ` | |
37 | assert.Equal(t, expected, string(y.([]byte))) | |
38 | ||
39 | nstd := `{"1":"the int key value","name":"a string value","y":"some value","tag":{"name":"tag name"}}` | |
40 | const nestpected = `"1": the int key value | |
41 | name: a string value | |
42 | y: some value | |
43 | tag: | |
44 | name: tag name | |
45 | ` | |
46 | var ndata JSONMapSlice | |
47 | require.NoError(t, json.Unmarshal([]byte(nstd), &ndata)) | |
48 | ny, err := ndata.MarshalYAML() | |
49 | require.NoError(t, err) | |
50 | assert.Equal(t, nestpected, string(ny.([]byte))) | |
51 | ||
52 | ydoc, err := BytesToYAMLDoc([]byte(fixtures2224)) | |
53 | require.NoError(t, err) | |
54 | b, err := YAMLToJSON(ydoc) | |
55 | require.NoError(t, err) | |
56 | ||
57 | var bdata JSONMapSlice | |
58 | require.NoError(t, json.Unmarshal(b, &bdata)) | |
59 | ||
27 | 60 | } |
28 | ||
29 | func (f failJSONMarshal) MarshalJSON() ([]byte, error) { | |
30 | return nil, errors.New("expected") | |
31 | } | |
32 | */ | |
33 | 61 | |
34 | 62 | func TestYAMLToJSON(t *testing.T) { |
35 | 63 | |
38 | 66 | name: a string value |
39 | 67 | 'y': some value |
40 | 68 | ` |
41 | var data yaml.MapSlice | |
69 | var data yaml.Node | |
42 | 70 | _ = yaml.Unmarshal([]byte(sd), &data) |
43 | 71 | |
44 | 72 | d, err := YAMLToJSON(data) |
46 | 74 | assert.Equal(t, `{"1":"the int key value","name":"a string value","y":"some value"}`, string(d)) |
47 | 75 | } |
48 | 76 | |
49 | data = append(data, yaml.MapItem{Key: true, Value: "the bool value"}) | |
77 | ns := []*yaml.Node{ | |
78 | { | |
79 | Kind: yaml.ScalarNode, | |
80 | Value: "true", | |
81 | Tag: "!!bool", | |
82 | }, | |
83 | { | |
84 | Kind: yaml.ScalarNode, | |
85 | Value: "the bool value", | |
86 | Tag: "!!str", | |
87 | }, | |
88 | } | |
89 | data.Content[0].Content = append(data.Content[0].Content, ns...) | |
50 | 90 | d, err = YAMLToJSON(data) |
51 | 91 | assert.Error(t, err) |
52 | 92 | assert.Nil(t, d) |
53 | 93 | |
54 | data = data[:len(data)-1] | |
55 | ||
56 | tag := yaml.MapSlice{{Key: "name", Value: "tag name"}} | |
57 | data = append(data, yaml.MapItem{Key: "tag", Value: tag}) | |
94 | data.Content[0].Content = data.Content[0].Content[:len(data.Content[0].Content)-2] | |
95 | ||
96 | tag := []*yaml.Node{ | |
97 | { | |
98 | Kind: yaml.ScalarNode, | |
99 | Value: "tag", | |
100 | Tag: "!!str", | |
101 | }, | |
102 | { | |
103 | Kind: yaml.MappingNode, | |
104 | Content: []*yaml.Node{ | |
105 | { | |
106 | Kind: yaml.ScalarNode, | |
107 | Value: "name", | |
108 | Tag: "!!str", | |
109 | }, | |
110 | { | |
111 | Kind: yaml.ScalarNode, | |
112 | Value: "tag name", | |
113 | Tag: "!!str", | |
114 | }, | |
115 | }, | |
116 | }, | |
117 | } | |
118 | data.Content[0].Content = append(data.Content[0].Content, tag...) | |
58 | 119 | |
59 | 120 | d, err = YAMLToJSON(data) |
60 | 121 | assert.NoError(t, err) |
61 | 122 | assert.Equal(t, `{"1":"the int key value","name":"a string value","y":"some value","tag":{"name":"tag name"}}`, string(d)) |
62 | 123 | |
63 | tag = yaml.MapSlice{{Key: true, Value: "bool tag name"}} | |
64 | data = append(data[:len(data)-1], yaml.MapItem{Key: "tag", Value: tag}) | |
124 | tag[1].Content = []*yaml.Node{ | |
125 | { | |
126 | Kind: yaml.ScalarNode, | |
127 | Value: "true", | |
128 | Tag: "!!bool", | |
129 | }, | |
130 | { | |
131 | Kind: yaml.ScalarNode, | |
132 | Value: "the bool tag name", | |
133 | Tag: "!!str", | |
134 | }, | |
135 | } | |
65 | 136 | |
66 | 137 | d, err = YAMLToJSON(data) |
67 | 138 | assert.Error(t, err) |
103 | 174 | doc, err := BytesToYAMLDoc([]byte(withYKey)) |
104 | 175 | if assert.NoError(t, err) { |
105 | 176 | _, err := YAMLToJSON(doc) |
106 | if assert.Error(t, err) { | |
177 | if assert.NoError(t, err) { | |
107 | 178 | doc, err := BytesToYAMLDoc([]byte(withQuotedYKey)) |
108 | 179 | if assert.NoError(t, err) { |
109 | 180 | jsond, err := YAMLToJSON(doc) |
130 | 201 | } |
131 | 202 | |
132 | 203 | func TestMapKeyTypes(t *testing.T) { |
133 | d := yaml.MapSlice{ | |
134 | yaml.MapItem{Key: 12345, Value: "int"}, | |
135 | yaml.MapItem{Key: int8(1), Value: "int8"}, | |
136 | yaml.MapItem{Key: int16(12345), Value: "int16"}, | |
137 | yaml.MapItem{Key: int32(12345678), Value: "int32"}, | |
138 | yaml.MapItem{Key: int64(12345678910), Value: "int64"}, | |
139 | yaml.MapItem{Key: uint(12345), Value: "uint"}, | |
140 | yaml.MapItem{Key: uint8(1), Value: "uint8"}, | |
141 | yaml.MapItem{Key: uint16(12345), Value: "uint16"}, | |
142 | yaml.MapItem{Key: uint32(12345678), Value: "uint32"}, | |
143 | yaml.MapItem{Key: uint64(12345678910), Value: "uint64"}, | |
144 | } | |
145 | _, err := YAMLToJSON(d) | |
146 | assert.NoError(t, err) | |
147 | ||
148 | 204 | dm := map[interface{}]interface{}{ |
149 | 205 | 12345: "int", |
150 | 206 | int8(1): "int8", |
157 | 213 | uint32(12345678): "uint32", |
158 | 214 | uint64(12345678910): "uint64", |
159 | 215 | } |
160 | _, err = YAMLToJSON(dm) | |
216 | _, err := YAMLToJSON(dm) | |
161 | 217 | assert.NoError(t, err) |
162 | 218 | } |
219 | ||
220 | const fixtures2224 = `definitions: | |
221 | Time: | |
222 | type: string | |
223 | format: date-time | |
224 | x-go-type: | |
225 | import: | |
226 | package: time | |
227 | embedded: true | |
228 | type: Time | |
229 | x-nullable: true | |
230 | ||
231 | TimeAsObject: # <- time.Time is actually a struct | |
232 | type: string | |
233 | format: date-time | |
234 | x-go-type: | |
235 | import: | |
236 | package: time | |
237 | hints: | |
238 | kind: object | |
239 | embedded: true | |
240 | type: Time | |
241 | x-nullable: true | |
242 | ||
243 | Raw: | |
244 | x-go-type: | |
245 | import: | |
246 | package: encoding/json | |
247 | hints: | |
248 | kind: primitive | |
249 | embedded: true | |
250 | type: RawMessage | |
251 | ||
252 | Request: | |
253 | x-go-type: | |
254 | import: | |
255 | package: net/http | |
256 | hints: | |
257 | kind: object | |
258 | embedded: true | |
259 | type: Request | |
260 | ||
261 | RequestPointer: | |
262 | x-go-type: | |
263 | import: | |
264 | package: net/http | |
265 | hints: | |
266 | kind: object | |
267 | nullable: true | |
268 | embedded: true | |
269 | type: Request | |
270 | ||
271 | OldStyleImport: | |
272 | type: object | |
273 | x-go-type: | |
274 | import: | |
275 | package: net/http | |
276 | type: Request | |
277 | hints: | |
278 | noValidation: true | |
279 | ||
280 | OldStyleRenamed: | |
281 | type: object | |
282 | x-go-type: | |
283 | import: | |
284 | package: net/http | |
285 | type: Request | |
286 | hints: | |
287 | noValidation: true | |
288 | x-go-name: OldRenamed | |
289 | ||
290 | ObjectWithEmbedded: | |
291 | type: object | |
292 | properties: | |
293 | a: | |
294 | $ref: '#/definitions/Time' | |
295 | b: | |
296 | $ref: '#/definitions/Request' | |
297 | c: | |
298 | $ref: '#/definitions/TimeAsObject' | |
299 | d: | |
300 | $ref: '#/definitions/Raw' | |
301 | e: | |
302 | $ref: '#/definitions/JSONObject' | |
303 | f: | |
304 | $ref: '#/definitions/JSONMessage' | |
305 | g: | |
306 | $ref: '#/definitions/JSONObjectWithAlias' | |
307 | ||
308 | ObjectWithExternals: | |
309 | type: object | |
310 | properties: | |
311 | a: | |
312 | $ref: '#/definitions/OldStyleImport' | |
313 | b: | |
314 | $ref: '#/definitions/OldStyleRenamed' | |
315 | ||
316 | Base: | |
317 | properties: &base | |
318 | id: | |
319 | type: integer | |
320 | format: uint64 | |
321 | x-go-custom-tag: 'gorm:"primary_key"' | |
322 | FBID: | |
323 | type: integer | |
324 | format: uint64 | |
325 | x-go-custom-tag: 'gorm:"index"' | |
326 | created_at: | |
327 | $ref: "#/definitions/Time" | |
328 | updated_at: | |
329 | $ref: "#/definitions/Time" | |
330 | version: | |
331 | type: integer | |
332 | format: uint64 | |
333 | ||
334 | HotspotType: | |
335 | type: string | |
336 | enum: | |
337 | - A | |
338 | - B | |
339 | - C | |
340 | ||
341 | Hotspot: | |
342 | type: object | |
343 | allOf: | |
344 | - properties: *base | |
345 | - properties: | |
346 | access_points: | |
347 | type: array | |
348 | items: | |
349 | $ref: '#/definitions/AccessPoint' | |
350 | type: | |
351 | $ref: '#/definitions/HotspotType' | |
352 | required: | |
353 | - type | |
354 | ||
355 | AccessPoint: | |
356 | type: object | |
357 | allOf: | |
358 | - properties: *base | |
359 | - properties: | |
360 | mac_address: | |
361 | type: string | |
362 | x-go-custom-tag: 'gorm:"index;not null;unique"' | |
363 | hotspot_id: | |
364 | type: integer | |
365 | format: uint64 | |
366 | hotspot: | |
367 | $ref: '#/definitions/Hotspot' | |
368 | ||
369 | JSONObject: | |
370 | type: object | |
371 | additionalProperties: | |
372 | type: array | |
373 | items: | |
374 | $ref: '#/definitions/Raw' | |
375 | ||
376 | JSONObjectWithAlias: | |
377 | type: object | |
378 | additionalProperties: | |
379 | type: object | |
380 | properties: | |
381 | message: | |
382 | $ref: '#/definitions/JSONMessage' | |
383 | ||
384 | JSONMessage: | |
385 | $ref: '#/definitions/Raw' | |
386 | ||
387 | Incorrect: | |
388 | x-go-type: | |
389 | import: | |
390 | package: net | |
391 | hints: | |
392 | kind: array | |
393 | embedded: true | |
394 | type: Buffers | |
395 | x-nullable: true | |
396 | ` | |
163 | 397 | |
164 | 398 | const withQuotedYKey = `consumes: |
165 | 399 | - application/json |