From c4a20ccae7cd540713ae9bd2aa63ae605841a753 Mon Sep 17 00:00:00 2001 From: decentral1se Date: Tue, 17 Jan 2023 11:15:01 +0100 Subject: [PATCH] feat: init --- .goreleaser.yml | 16 + LICENSE | 15 + README.md | 47 + go.mod | 50 + go.sum | 157 +++ kimchi.go | 401 ++++++ strfmt/.editorconfig | 26 + strfmt/.gitattributes | 2 + strfmt/.github/CONTRIBUTING.md | 117 ++ strfmt/.github/workflows/ci.yaml | 46 + strfmt/.gitignore | 2 + strfmt/.golangci.yml | 50 + strfmt/CODE_OF_CONDUCT.md | 74 ++ strfmt/LICENSE | 202 +++ strfmt/README.md | 88 ++ strfmt/bson.go | 165 +++ strfmt/bson_test.go | 68 + strfmt/conv/date.go | 18 + strfmt/conv/date_test.go | 16 + strfmt/conv/default.go | 305 +++++ strfmt/conv/default_test.go | 129 ++ strfmt/conv/duration.go | 18 + strfmt/conv/duration_test.go | 15 + strfmt/conv/time.go | 18 + strfmt/conv/time_test.go | 16 + strfmt/conv/ulid.go | 18 + strfmt/conv/ulid_test.go | 22 + strfmt/date.go | 187 +++ strfmt/date_test.go | 175 +++ strfmt/default.go | 2035 ++++++++++++++++++++++++++++++ strfmt/default_test.go | 776 ++++++++++++ strfmt/doc.go | 18 + strfmt/duration.go | 211 ++++ strfmt/duration_test.go | 225 ++++ strfmt/format.go | 326 +++++ strfmt/format_test.go | 306 +++++ strfmt/go.mod | 13 + strfmt/go.sum | 63 + strfmt/hack/coverage | 20 + strfmt/time.go | 297 +++++ strfmt/time_test.go | 326 +++++ strfmt/ulid.go | 225 ++++ strfmt/ulid_test.go | 343 +++++ 43 files changed, 7647 insertions(+) create mode 100644 .goreleaser.yml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 go.mod create mode 100644 go.sum create mode 100644 kimchi.go create mode 100644 strfmt/.editorconfig create mode 100644 strfmt/.gitattributes create mode 100644 strfmt/.github/CONTRIBUTING.md create mode 100644 strfmt/.github/workflows/ci.yaml create mode 100644 strfmt/.gitignore create mode 100644 strfmt/.golangci.yml create mode 100644 strfmt/CODE_OF_CONDUCT.md create mode 100644 strfmt/LICENSE create mode 100644 strfmt/README.md create mode 100644 strfmt/bson.go create mode 100644 strfmt/bson_test.go create mode 100644 strfmt/conv/date.go create mode 100644 strfmt/conv/date_test.go create mode 100644 strfmt/conv/default.go create mode 100644 strfmt/conv/default_test.go create mode 100644 strfmt/conv/duration.go create mode 100644 strfmt/conv/duration_test.go create mode 100644 strfmt/conv/time.go create mode 100644 strfmt/conv/time_test.go create mode 100644 strfmt/conv/ulid.go create mode 100644 strfmt/conv/ulid_test.go create mode 100644 strfmt/date.go create mode 100644 strfmt/date_test.go create mode 100644 strfmt/default.go create mode 100644 strfmt/default_test.go create mode 100644 strfmt/doc.go create mode 100644 strfmt/duration.go create mode 100644 strfmt/duration_test.go create mode 100644 strfmt/format.go create mode 100644 strfmt/format_test.go create mode 100644 strfmt/go.mod create mode 100644 strfmt/go.sum create mode 100755 strfmt/hack/coverage create mode 100644 strfmt/time.go create mode 100644 strfmt/time_test.go create mode 100644 strfmt/ulid.go create mode 100644 strfmt/ulid_test.go diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..127349e --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,16 @@ +--- +project_name: kimchi + +before: + hooks: + - go mod tidy + +builds: + - goos: + - linux + - darwin + goarch: + - amd64 + +archives: + - format: binary diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f904372 --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +kimchi: Simple Kimai terminal client ๐ŸคŸ +Copyright (C) 2023 Autonomic Co-op + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . diff --git a/README.md b/README.md new file mode 100644 index 0000000..ef18715 --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# `kimchi` + +> Simple Kimai terminal client ๐ŸคŸ + +## Usage + +1. Grab a binary from [`dist`](./dist) that matches your OS +1. Download and `chmod +x` it +1. `kimchi -h` to see the help output + +## Hacking + +- `go build -v .` to build +- `go run kimchi.go` to test quickly +- Use `export DEBUG=1` to see every request/response + +## Release + +1. `goreleaser release --snapshot --rm-dist` +1. `git add . && git commit -m "release: new binaries" && git push` + +## Forks + +Had to fork [`strfmt`](https://github.com/go-openapi/strfmt) due to [`go-openapi/strfmt#92`](https://github.com/go-openapi/strfmt/issues/92). + +Also had to apply this diff: + +```diff +diff --git a/strfmt/time.go b/strfmt/time.go +index 748ca40..5dab792 100644 +--- a/strfmt/time.go ++++ b/strfmt/time.go +@@ -83,7 +83,8 @@ var ( + DateTimeFormats = []string{RFC3339Micro, RFC3339MicroNoColon, RFC3339Millis, RFC3339MillisNoColon, time.RFC3339, time.RFC3339Nano, ISO8601LocalTime, ISO8601PlusTimezone, ISO8601TimeWithReducedPrecision, ISO8601TimeWithReducedPrecisionLocaltime, ISO8601TimeUniversalSortableDateTimePattern} + + // MarshalFormat sets the time resolution format used for marshaling time (set to milliseconds) +- MarshalFormat = RFC3339Millis ++ // MarshalFormat = RFC3339Millis ++ MarshalFormat = ISO8601LocalTime + + // NormalizeTimeForMarshal provides a normalization function on time befeore marshalling (e.g. time.UTC). + // By default, the time value is not changed. +``` + +## License + + diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1a55c9d --- /dev/null +++ b/go.mod @@ -0,0 +1,50 @@ +module decentral1se/kimchi + +go 1.18 + +replace github.com/go-openapi/strfmt => ./strfmt + +require ( + github.com/AlecAivazis/survey/v2 v2.3.6 + github.com/decentral1se/go-kimai v0.0.0-20230118194531-adcc0c7317bc + github.com/go-openapi/runtime v0.25.0 + github.com/go-openapi/strfmt v0.21.3 + github.com/urfave/cli/v2 v2.23.7 +) + +require ( + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/analysis v0.21.2 // indirect + github.com/go-openapi/errors v0.20.3 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/loads v0.21.1 // indirect + github.com/go-openapi/spec v0.20.4 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-openapi/validate v0.22.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.2 // indirect + github.com/mattn/go-isatty v0.0.8 // indirect + github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect + github.com/mitchellh/mapstructure v1.4.3 // indirect + github.com/oklog/ulid v1.3.1 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + go.mongodb.org/mongo-driver v1.11.1 // indirect + go.opentelemetry.io/otel v1.11.1 // indirect + go.opentelemetry.io/otel/trace v1.11.1 // indirect + golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect + golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + golang.org/x/text v0.3.7 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e9bb4aa --- /dev/null +++ b/go.sum @@ -0,0 +1,157 @@ +github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw= +github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI= +github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= +github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= +github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= +github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decentral1se/go-kimai v0.0.0-20230118194531-adcc0c7317bc h1:jZTu+7km2qVE+IgISG1N10NbrWuTrwWYAWLuRIB5rCY= +github.com/decentral1se/go-kimai v0.0.0-20230118194531-adcc0c7317bc/go.mod h1:JLNqCfdxIc/AtnGGksa3CwJoE6ib5FmssdLzl5t1/HI= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/analysis v0.21.2 h1:hXFrOYFHUAMQdu6zwAiKKJHJQ8kqZs1ux/ru1P1wLJU= +github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= +github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.3 h1:rz6kiC84sqNQoqrtulzaL/VERgkoCyB6WdEkc2ujzUc= +github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/loads v0.21.1 h1:Wb3nVZpdEzDTcly8S4HMkey6fjARRzb7iEaySimlDW0= +github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= +github.com/go-openapi/runtime v0.25.0 h1:7yQTCdRbWhX8vnIjdzU8S00tBYf7Sg71EBeorlPHvhc= +github.com/go-openapi/runtime v0.25.0/go.mod h1:Ux6fikcHXyyob6LNWxtE96hWwjBPYF0DXgVFuMTneOs= +github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/validate v0.22.0 h1:b0QecH6VslW/TxtpKgzpO1SNG7GU2FsaqKdP1E2T50Y= +github.com/go-openapi/validate v0.22.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= +github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= +github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/urfave/cli/v2 v2.23.7 h1:YHDQ46s3VghFHFf1DdF+Sh7H4RqhcM+t0TmZRJx4oJY= +github.com/urfave/cli/v2 v2.23.7/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= +go.mongodb.org/mongo-driver v1.11.1 h1:QP0znIRTuL0jf1oBQoAoM0C6ZJfBK4kx0Uumtv1A7w8= +go.mongodb.org/mongo-driver v1.11.1/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= +go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4= +go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE= +go.opentelemetry.io/otel/sdk v1.11.1 h1:F7KmQgoHljhUuJyA+9BiU+EkJfyX5nVVF4wyzWZpKxs= +go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ= +go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/kimchi.go b/kimchi.go new file mode 100644 index 0000000..46b94f4 --- /dev/null +++ b/kimchi.go @@ -0,0 +1,401 @@ +package main + +import ( + "context" + "fmt" + "io/ioutil" + "log" + "math" + "os" + "path" + "strconv" + "strings" + "time" + + "github.com/decentral1se/go-kimai/client" + "github.com/decentral1se/go-kimai/client/activity" + "github.com/decentral1se/go-kimai/client/customer" + "github.com/decentral1se/go-kimai/client/project" + "github.com/decentral1se/go-kimai/client/timesheet" + "github.com/decentral1se/go-kimai/models" + + "github.com/AlecAivazis/survey/v2" + "github.com/go-openapi/runtime" + httpTransport "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + "github.com/urfave/cli/v2" +) + +var dateFormat = "2006-01-02T15:04:05" + +// homePath is the configuration home directory path. +var homePath = os.ExpandEnv("$HOME/.kimchi") + +// confPath is the configuration file path. +var confPath = path.Join(homePath, "conf.txt") + +// config stores all user config at run-time. +type config struct { + auth runtime.ClientAuthInfoWriter + client *client.GoKimai + domain string + token string + username string + hourlyRate string +} + +// prompt feeds input from user into the configuration. +func (c *config) prompt() error { + usernamePrompt := &survey.Input{Message: "Kimai username?"} + if err := survey.AskOne(usernamePrompt, &c.username); err != nil { + log.Fatalf("Unable to read kimai username: %s", err) + } + + tokenPrompt := &survey.Password{Message: "Kimai token?"} + if err := survey.AskOne(tokenPrompt, &c.token); err != nil { + log.Fatalf("Unable to read kimai token: %s", err) + } + + hourlyPrompt := &survey.Input{Message: "Hourly rate?"} + if err := survey.AskOne(hourlyPrompt, &c.hourlyRate); err != nil { + log.Fatalf("Unable to read hourly rate: %s", err) + } + + if err := c.save(); err != nil { + log.Fatalf("Unable to write config to the FS: %s", err) + } + + return nil +} + +// toBytes converts a configuration to bytes. +func (c *config) toBytes() []byte { + content := "" + content += fmt.Sprintf("%s\n", c.username) + content += fmt.Sprintf("%s\n", c.token) + content += fmt.Sprintf("%s\n", c.domain) + content += fmt.Sprintf("%s\n", c.hourlyRate) + return []byte(content) +} + +// save writes a config to the FS as plain text. +func (c config) save() error { + if err := ioutil.WriteFile(confPath, c.toBytes(), 0644); err != nil { + return fmt.Errorf("Unable to write %s: %s", confPath, err) + } + + return nil +} + +// load reads a config from the FS. +func (c *config) load() error { + contents, err := ioutil.ReadFile(confPath) + if err != nil { + return fmt.Errorf("Unable to read %s: %s", confPath, err) + } + + lines := strings.Split(string(contents), "\n") + if len(lines) < 4 { + return fmt.Errorf("Unable to read %s, it's badly formatted?", confPath) + } + + c.username = lines[0] + c.token = lines[1] + c.domain = lines[2] + c.hourlyRate = lines[3] + + return nil +} + +func (c *config) initClient() { + c.auth = httpTransport.Compose( + httpTransport.APIKeyAuth("X-AUTH-USER", "header", c.username), + httpTransport.APIKeyAuth("X-AUTH-TOKEN", "header", c.token), + ) + transport := httpTransport.New(c.domain, "", []string{"https"}) + c.client = client.New(transport, strfmt.Default) +} + +// selectCustomer allows uers to select a single customer. +func selectCustomer(conf *config) (*models.CustomerCollection, error) { + params := customer.NewGetAPICustomersParams() + params.WithOrder("ASC") + params.WithOrderBy("name") + + resp, err := conf.client.Customer.GetAPICustomers(params, conf.auth) + if err != nil { + return &models.CustomerCollection{}, fmt.Errorf("Unable to retrieve customers: %s", err) + } + + var names []string + for _, customer := range resp.Payload { + names = append(names, *customer.Name) + } + + prompt := &survey.Select{ + Message: "Select a customer:", + Options: names, + } + + var choice string + if err := survey.AskOne(prompt, &choice, survey.WithPageSize(20)); err != nil { + return &models.CustomerCollection{}, fmt.Errorf("Unable to choose customer name: %s", err) + } + + var chosenCustomer *models.CustomerCollection + for _, customer := range resp.Payload { + if choice == *customer.Name { + chosenCustomer = customer + } + } + + return chosenCustomer, nil +} + +// selectProject allows uers to select a single project. +func selectProject(conf *config, customer *models.CustomerCollection) (*models.ProjectCollection, error) { + params := project.NewGetAPIProjectsParams() + params.Customer = strconv.FormatInt(customer.ID, 10) + params.IgnoreDates = "1" + params.GlobalActivities = "1" + params.WithOrder("ASC") + params.WithOrderBy("name") + + resp, err := conf.client.Project.GetAPIProjects(params, conf.auth) + if err != nil { + return &models.ProjectCollection{}, fmt.Errorf("Unable to retrieve projects: %s", err) + } + + var names []string + for _, project := range resp.Payload { + names = append(names, *project.Name) + } + + prompt := &survey.Select{ + Message: "Select a project:", + Options: names, + } + + var choice string + if err := survey.AskOne(prompt, &choice, survey.WithPageSize(20)); err != nil { + return &models.ProjectCollection{}, fmt.Errorf("Unable to choose project: %s", err) + } + + var chosenProject *models.ProjectCollection + for _, project := range resp.Payload { + if choice == *project.Name { + chosenProject = project + } + } + + return chosenProject, nil +} + +// selectActivity allows uers to select a single activity. +func selectActivity(conf *config, project *models.ProjectCollection) (*models.ActivityCollection, error) { + params := activity.NewGetAPIActivitiesParams() + params.Project = strconv.FormatInt(project.ID, 10) + params.WithOrder("ASC") + params.WithOrderBy("name") + + resp, err := conf.client.Activity.GetAPIActivities(params, conf.auth) + if err != nil { + return &models.ActivityCollection{}, fmt.Errorf("Unable to retrieve activities: %s", err) + } + + var names []string + for _, activity := range resp.Payload { + names = append(names, *activity.Name) + } + + prompt := &survey.Select{ + Message: "Select an activity:", + Options: names, + } + + var choice string + if err := survey.AskOne(prompt, &choice, survey.WithPageSize(20)); err != nil { + return &models.ActivityCollection{}, fmt.Errorf("Unable to choose an activity: %s", err) + } + + var chosenActivity *models.ActivityCollection + for _, activity := range resp.Payload { + if choice == *activity.Name { + chosenActivity = activity + } + } + + return chosenActivity, nil +} + +func main() { + app := &cli.App{ + Name: "kimchi", + Usage: "Simple Kimai terminal client ๐ŸคŸ", + Description: "Run without sub-commands for current status.", + Before: func(c *cli.Context) error { + conf := config{domain: "kimai.autonomic.zone"} + if err := os.Mkdir(homePath, 0764); err != nil { + if !os.IsExist(err) { + log.Fatalf("Unable to create ~/.kimchi: %s", err) + } + } + + if err := conf.load(); err != nil { + if err := conf.prompt(); err != nil { + log.Fatalf("Unable to load config from the FS: %s", err) + } + } + + c.Context = context.WithValue(c.Context, "config", conf) + + return nil + }, + Commands: []*cli.Command{ + { + Name: "start", + Aliases: []string{"s"}, + Description: "Fuzzy search enabled interface.", + Usage: "Start tracking time", + Action: func(cCtx *cli.Context) error { + conf := cCtx.Context.Value("config").(config) + conf.initClient() + + customer, err := selectCustomer(&conf) + if err != nil { + log.Fatal(err) + } + + project, err := selectProject(&conf, customer) + if err != nil { + log.Fatal(err) + } + + activity, err := selectActivity(&conf, project) + if err != nil { + log.Fatal(err) + } + + var description string + descPrompt := &survey.Input{Message: "Description (leave empty to ignore):"} + if err := survey.AskOne(descPrompt, &description); err != nil { + log.Fatalf("Unable to read description: %s", err) + } + + var hourly string + hourlyPrompt := &survey.Input{Message: "Hourly:", Default: conf.hourlyRate} + if err := survey.AskOne(hourlyPrompt, &hourly); err != nil { + log.Fatalf("Unable to read hourly: %s", err) + } + + parsedHourly, err := strconv.ParseFloat(hourly, 32) + if err != nil { + log.Fatalf("Unable to determine hourly rate: %s", err) + } + parsedHourly = math.Round(parsedHourly*100) / 100 // round to closest + + now := strfmt.DateTime(time.Now()) + + startParams := timesheet.NewPostAPITimesheetsParams() + startParams.WithBody(&models.TimesheetEditForm{ + Project: &project.ID, + Activity: &activity.ID, + Begin: &now, + HourlyRate: parsedHourly, + }) + + if description != "" { + startParams.Body.Description = description + } + + _, err = conf.client.Timesheet.PostAPITimesheets(startParams, conf.auth) + if err != nil { + log.Fatalf("Unable to start timesheet entry: %s", err) + } + + fmt.Println("Started tracking ๐Ÿ‘") + + return nil + }, + }, + { + Name: "stop", + Aliases: []string{"t"}, + Usage: "Stop tracking time", + Action: func(cCtx *cli.Context) error { + conf := cCtx.Context.Value("config").(config) + conf.initClient() + + timesheetParams := timesheet.NewGetAPITimesheetsActiveParams() + tsResp, err := conf.client.Timesheet.GetAPITimesheetsActive(timesheetParams, conf.auth) + if err != nil { + log.Fatalf("Unable to retrieve active timesheet: %s", err) + } + + if len(tsResp.Payload) == 0 { + fmt.Println("No active timesheets currently") + return nil + } + + id := tsResp.Payload[0].ID + stopParams := timesheet.NewPatchAPITimesheetsIDStopParams() + stopParams.ID = id + + _, err = conf.client.Timesheet.PatchAPITimesheetsIDStop(stopParams, conf.auth) + if err != nil { + log.Fatalf("Unable to stop active timesheet: %s", err) + } + + fmt.Println("Tracking stopped ๐Ÿคš") + + return nil + }, + }, + }, + Action: func(c *cli.Context) error { + if c.Args().Len() > 0 { // error out with incorrect sub-commands + if err := cli.ShowSubcommandHelp(c); err != nil { + log.Fatal(err) + } + os.Exit(1) + } + + conf := c.Context.Value("config").(config) + conf.initClient() + + timesheetParams := timesheet.NewGetAPITimesheetsActiveParams() + resp, err := conf.client.Timesheet.GetAPITimesheetsActive(timesheetParams, conf.auth) + if err != nil { + log.Fatalf("Unable to retrieve active timesheet: %s", err) + } + + if len(resp.Payload) == 0 { + fmt.Println("No active timesheets currently") + return nil + } + + active := resp.Payload[0] + + // FIXME have this show as a nicer duration + begin := time.Time(*active.Begin).Local() + + fmt.Printf("๐Ÿ‘‰ %s %s %s\n", + *active.Project.Customer.Name, + *active.Project.Name, + *active.Activity.Name, + ) + + if active.Description != "" { + fmt.Printf("๐Ÿ““ %s \n", active.Description) + } + + fmt.Printf("โณ %s", begin) + + return nil + }, + } + + if err := app.Run(os.Args); err != nil { + log.Fatalf("Woops, something went wrong: %s", err) + } +} diff --git a/strfmt/.editorconfig b/strfmt/.editorconfig new file mode 100644 index 0000000..3152da6 --- /dev/null +++ b/strfmt/.editorconfig @@ -0,0 +1,26 @@ +# 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 diff --git a/strfmt/.gitattributes b/strfmt/.gitattributes new file mode 100644 index 0000000..d020be8 --- /dev/null +++ b/strfmt/.gitattributes @@ -0,0 +1,2 @@ +*.go text eol=lf + diff --git a/strfmt/.github/CONTRIBUTING.md b/strfmt/.github/CONTRIBUTING.md new file mode 100644 index 0000000..7dea424 --- /dev/null +++ b/strfmt/.github/CONTRIBUTING.md @@ -0,0 +1,117 @@ +## Contribution Guidelines + +### Pull requests are always welcome + +We are always thrilled to receive pull requests, and do our best to +process them as fast as possible. Not sure if that typo is worth a pull +request? Do it! We will appreciate it. + +If your pull request is not accepted on the first try, don't be +discouraged! If there's a problem with the implementation, hopefully you +received feedback on what to improve. + +We're trying very hard to keep go-swagger lean and focused. We don't want it +to do everything for everybody. This means that we might decide against +incorporating a new feature. However, there might be a way to implement +that feature *on top of* go-swagger. + + +### Conventions + +Fork the repo and make changes on your fork in a feature branch: + +- If it's a bugfix branch, name it XXX-something where XXX is the number of the + issue +- If it's a feature branch, create an enhancement issue to announce your + intentions, and name it XXX-something where XXX is the number of the issue. + +Submit unit tests for your changes. Go has a great test framework built in; use +it! Take a look at existing tests for inspiration. Run the full test suite on +your branch before submitting a pull request. + +Update the documentation when creating or modifying features. Test +your documentation changes for clarity, concision, and correctness, as +well as a clean documentation build. See ``docs/README.md`` for more +information on building the docs and how docs get released. + +Write clean code. Universally formatted code promotes ease of writing, reading, +and maintenance. Always run `gofmt -s -w file.go` on each changed file before +committing your changes. Most editors have plugins that do this automatically. + +Pull requests descriptions should be as clear as possible and include a +reference to all the issues that they address. + +Pull requests must not contain commits from other users or branches. + +Commit messages must start with a capitalized and short summary (max. 50 +chars) written in the imperative, followed by an optional, more detailed +explanatory text which is separated from the summary by an empty line. + +Code review comments may be added to your pull request. Discuss, then make the +suggested modifications and push additional commits to your feature branch. Be +sure to post a comment after pushing. The new commits will show up in the pull +request automatically, but the reviewers will not be notified unless you +comment. + +Before the pull request is merged, make sure that you squash your commits into +logical units of work using `git rebase -i` and `git push -f`. After every +commit the test suite should be passing. Include documentation changes in the +same commit so that a revert would remove all traces of the feature or fix. + +Commits that fix or close an issue should include a reference like `Closes #XXX` +or `Fixes #XXX`, which will automatically close the issue when merged. + +### Sign your work + +The sign-off is a simple line at the end of the explanation for the +patch, which certifies that you wrote it or otherwise have the right to +pass it on as an open-source patch. The rules are pretty simple: if you +can certify the below (from +[developercertificate.org](http://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +using your real name (sorry, no pseudonyms or anonymous contributions.) + +You can add the sign off when creating the git commit via `git commit -s`. diff --git a/strfmt/.github/workflows/ci.yaml b/strfmt/.github/workflows/ci.yaml new file mode 100644 index 0000000..7d0b2c9 --- /dev/null +++ b/strfmt/.github/workflows/ci.yaml @@ -0,0 +1,46 @@ +name: Go + +on: [push, pull_request] + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ ubuntu-latest, macos-latest, windows-latest ] + fail-fast: false + + steps: + - uses: actions/checkout@v2 + + - name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: 1.17.1 + + - name: Setup gotestsum + uses: autero1/action-gotestsum@v1.0.0 + with: + gotestsum_version: 1.7.0 + + - name: Test + run: gotestsum --format short-verbose -- -race -timeout=20m -coverprofile=coverage_txt -covermode=atomic ./... + + - uses: codecov/codecov-action@v2 + with: + files: coverage_txt + + lint: + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ ubuntu-latest, macos-latest, windows-latest ] + fail-fast: false + + steps: + - uses: actions/checkout@v2 + - uses: golangci/golangci-lint-action@v2 + with: + args: --timeout=5m diff --git a/strfmt/.gitignore b/strfmt/.gitignore new file mode 100644 index 0000000..dd91ed6 --- /dev/null +++ b/strfmt/.gitignore @@ -0,0 +1,2 @@ +secrets.yml +coverage.out diff --git a/strfmt/.golangci.yml b/strfmt/.golangci.yml new file mode 100644 index 0000000..d36b256 --- /dev/null +++ b/strfmt/.golangci.yml @@ -0,0 +1,50 @@ +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + gocyclo: + min-complexity: 31 + maligned: + suggest-new: true + dupl: + threshold: 100 + goconst: + min-len: 2 + min-occurrences: 4 + +linters: + enable-all: true + disable: + - maligned + - lll + - gochecknoinits + - gochecknoglobals + - godox + - gocognit + - whitespace + - wsl + - funlen + - wrapcheck + - testpackage + - nlreturn + - gofumpt + - goerr113 + - gci + - gomnd + - godot + - exhaustivestruct + - paralleltest + - varnamelen + - ireturn + - exhaustruct + #- thelper + +issues: + exclude-rules: + - path: bson.go + text: "should be .*ObjectID" + linters: + - golint + - stylecheck + diff --git a/strfmt/CODE_OF_CONDUCT.md b/strfmt/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..9322b06 --- /dev/null +++ b/strfmt/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# 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/ diff --git a/strfmt/LICENSE b/strfmt/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/strfmt/LICENSE @@ -0,0 +1,202 @@ + + 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. diff --git a/strfmt/README.md b/strfmt/README.md new file mode 100644 index 0000000..0cf89d7 --- /dev/null +++ b/strfmt/README.md @@ -0,0 +1,88 @@ +# Strfmt [![Build Status](https://travis-ci.org/go-openapi/strfmt.svg?branch=master)](https://travis-ci.org/go-openapi/strfmt) [![codecov](https://codecov.io/gh/go-openapi/strfmt/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/strfmt) [![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/strfmt/master/LICENSE) +[![GoDoc](https://godoc.org/github.com/go-openapi/strfmt?status.svg)](http://godoc.org/github.com/go-openapi/strfmt) +[![GolangCI](https://golangci.com/badges/github.com/go-openapi/strfmt.svg)](https://golangci.com) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/strfmt)](https://goreportcard.com/report/github.com/go-openapi/strfmt) + +This package exposes a registry of data types to support string formats in the go-openapi toolkit. + +strfmt represents a well known string format such as credit card or email. The go toolkit for OpenAPI specifications knows how to deal with those. + +## Supported data formats +go-openapi/strfmt follows the swagger 2.0 specification with the following formats +defined [here](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types). + +It also provides convenient extensions to go-openapi users. + +- [x] JSON-schema draft 4 formats + - date-time + - email + - hostname + - ipv4 + - ipv6 + - uri +- [x] swagger 2.0 format extensions + - binary + - byte (e.g. base64 encoded string) + - date (e.g. "1970-01-01") + - password +- [x] go-openapi custom format extensions + - bsonobjectid (BSON objectID) + - creditcard + - duration (e.g. "3 weeks", "1ms") + - hexcolor (e.g. "#FFFFFF") + - isbn, isbn10, isbn13 + - mac (e.g "01:02:03:04:05:06") + - rgbcolor (e.g. "rgb(100,100,100)") + - ssn + - uuid, uuid3, uuid4, uuid5 + - cidr (e.g. "192.0.2.1/24", "2001:db8:a0b:12f0::1/32") + - ulid (e.g. "00000PP9HGSBSSDZ1JTEXBJ0PW", [spec](https://github.com/ulid/spec)) + +> NOTE: as the name stands for, this package is intended to support string formatting only. +> It does not provide validation for numerical values with swagger format extension for JSON types "number" or +> "integer" (e.g. float, double, int32...). + +## Type conversion + +All types defined here are stringers and may be converted to strings with `.String()`. +Note that most types defined by this package may be converted directly to string like `string(Email{})`. + +`Date` and `DateTime` may be converted directly to `time.Time` like `time.Time(Time{})`. +Similarly, you can convert `Duration` to `time.Duration` as in `time.Duration(Duration{})` + +## Using pointers + +The `conv` subpackage provides helpers to convert the types to and from pointers, just like `go-openapi/swag` does +with primitive types. + +## Format types +Types defined in strfmt expose marshaling and validation capabilities. + +List of defined types: +- Base64 +- CreditCard +- Date +- DateTime +- Duration +- Email +- HexColor +- Hostname +- IPv4 +- IPv6 +- CIDR +- ISBN +- ISBN10 +- ISBN13 +- MAC +- ObjectId +- Password +- RGBColor +- SSN +- URI +- UUID +- UUID3 +- UUID4 +- UUID5 +- [ULID](https://github.com/ulid/spec) diff --git a/strfmt/bson.go b/strfmt/bson.go new file mode 100644 index 0000000..8740b15 --- /dev/null +++ b/strfmt/bson.go @@ -0,0 +1,165 @@ +// 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 strfmt + +import ( + "database/sql/driver" + "fmt" + + "go.mongodb.org/mongo-driver/bson" + + "go.mongodb.org/mongo-driver/bson/bsontype" + bsonprim "go.mongodb.org/mongo-driver/bson/primitive" +) + +func init() { + var id ObjectId + // register this format in the default registry + Default.Add("bsonobjectid", &id, IsBSONObjectID) +} + +// IsBSONObjectID returns true when the string is a valid BSON.ObjectId +func IsBSONObjectID(str string) bool { + _, err := bsonprim.ObjectIDFromHex(str) + return err == nil +} + +// ObjectId represents a BSON object ID (alias to go.mongodb.org/mongo-driver/bson/primitive.ObjectID) +// +// swagger:strfmt bsonobjectid +type ObjectId bsonprim.ObjectID //nolint:revive + +// NewObjectId creates a ObjectId from a Hex String +func NewObjectId(hex string) ObjectId { //nolint:revive + oid, err := bsonprim.ObjectIDFromHex(hex) + if err != nil { + panic(err) + } + return ObjectId(oid) +} + +// MarshalText turns this instance into text +func (id ObjectId) MarshalText() ([]byte, error) { + oid := bsonprim.ObjectID(id) + if oid == bsonprim.NilObjectID { + return nil, nil + } + return []byte(oid.Hex()), nil +} + +// UnmarshalText hydrates this instance from text +func (id *ObjectId) UnmarshalText(data []byte) error { // validation is performed later on + if len(data) == 0 { + *id = ObjectId(bsonprim.NilObjectID) + return nil + } + oidstr := string(data) + oid, err := bsonprim.ObjectIDFromHex(oidstr) + if err != nil { + return err + } + *id = ObjectId(oid) + return nil +} + +// Scan read a value from a database driver +func (id *ObjectId) Scan(raw interface{}) error { + var data []byte + switch v := raw.(type) { + case []byte: + data = v + case string: + data = []byte(v) + default: + return fmt.Errorf("cannot sql.Scan() strfmt.URI from: %#v", v) + } + + return id.UnmarshalText(data) +} + +// Value converts a value to a database driver value +func (id ObjectId) Value() (driver.Value, error) { + return driver.Value(bsonprim.ObjectID(id).Hex()), nil +} + +func (id ObjectId) String() string { + return bsonprim.ObjectID(id).Hex() +} + +// MarshalJSON returns the ObjectId as JSON +func (id ObjectId) MarshalJSON() ([]byte, error) { + return bsonprim.ObjectID(id).MarshalJSON() +} + +// UnmarshalJSON sets the ObjectId from JSON +func (id *ObjectId) UnmarshalJSON(data []byte) error { + var obj bsonprim.ObjectID + if err := obj.UnmarshalJSON(data); err != nil { + return err + } + *id = ObjectId(obj) + return nil +} + +// MarshalBSON renders the object id as a BSON document +func (id ObjectId) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": bsonprim.ObjectID(id)}) +} + +// UnmarshalBSON reads the objectId from a BSON document +func (id *ObjectId) UnmarshalBSON(data []byte) error { + var obj struct { + Data bsonprim.ObjectID + } + if err := bson.Unmarshal(data, &obj); err != nil { + return err + } + *id = ObjectId(obj.Data) + return nil +} + +// MarshalBSONValue is an interface implemented by types that can marshal themselves +// into a BSON document represented as bytes. The bytes returned must be a valid +// BSON document if the error is nil. +func (id ObjectId) MarshalBSONValue() (bsontype.Type, []byte, error) { + oid := bsonprim.ObjectID(id) + return bsontype.ObjectID, oid[:], nil +} + +// UnmarshalBSONValue is an interface implemented by types that can unmarshal a +// BSON value representation of themselves. The BSON bytes and type can be +// assumed to be valid. UnmarshalBSONValue must copy the BSON value bytes if it +// wishes to retain the data after returning. +func (id *ObjectId) UnmarshalBSONValue(tpe bsontype.Type, data []byte) error { + var oid bsonprim.ObjectID + copy(oid[:], data) + *id = ObjectId(oid) + return nil +} + +// DeepCopyInto copies the receiver and writes its value into out. +func (id *ObjectId) DeepCopyInto(out *ObjectId) { + *out = *id +} + +// DeepCopy copies the receiver into a new ObjectId. +func (id *ObjectId) DeepCopy() *ObjectId { + if id == nil { + return nil + } + out := new(ObjectId) + id.DeepCopyInto(out) + return out +} diff --git a/strfmt/bson_test.go b/strfmt/bson_test.go new file mode 100644 index 0000000..0dd971e --- /dev/null +++ b/strfmt/bson_test.go @@ -0,0 +1,68 @@ +// 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 strfmt + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/bson" +) + +func TestBSONObjectId_fullCycle(t *testing.T) { + id := NewObjectId("507f1f77bcf86cd799439011") + bytes, err := id.MarshalText() + assert.NoError(t, err) + + var idCopy ObjectId + + err = idCopy.Scan(bytes) + assert.NoError(t, err) + assert.Equal(t, id, idCopy) + + err = idCopy.UnmarshalText(bytes) + assert.NoError(t, err) + assert.Equal(t, id, idCopy) + + jsonBytes, err := id.MarshalJSON() + assert.NoError(t, err) + + err = idCopy.UnmarshalJSON(jsonBytes) + assert.NoError(t, err) + assert.Equal(t, id, idCopy) + + bsonBytes, err := bson.Marshal(&id) + assert.NoError(t, err) + + err = bson.Unmarshal(bsonBytes, &idCopy) + assert.NoError(t, err) + assert.Equal(t, id, idCopy) +} + +func TestDeepCopyObjectId(t *testing.T) { + id := NewObjectId("507f1f77bcf86cd799439011") + in := &id + + out := new(ObjectId) + in.DeepCopyInto(out) + assert.Equal(t, in, out) + + out2 := in.DeepCopy() + assert.Equal(t, in, out2) + + var inNil *ObjectId + out3 := inNil.DeepCopy() + assert.Nil(t, out3) +} diff --git a/strfmt/conv/date.go b/strfmt/conv/date.go new file mode 100644 index 0000000..b5c3820 --- /dev/null +++ b/strfmt/conv/date.go @@ -0,0 +1,18 @@ +package conv + +import "github.com/go-openapi/strfmt" + +// Date returns a pointer to of the Date value passed in. +func Date(v strfmt.Date) *strfmt.Date { + return &v +} + +// DateValue returns the value of the Date pointer passed in or +// the default value if the pointer is nil. +func DateValue(v *strfmt.Date) strfmt.Date { + if v == nil { + return strfmt.Date{} + } + + return *v +} diff --git a/strfmt/conv/date_test.go b/strfmt/conv/date_test.go new file mode 100644 index 0000000..85586f4 --- /dev/null +++ b/strfmt/conv/date_test.go @@ -0,0 +1,16 @@ +package conv + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/go-openapi/strfmt" +) + +func TestDateValue(t *testing.T) { + assert.Equal(t, strfmt.Date{}, DateValue(nil)) + date := strfmt.Date(time.Now()) + assert.Equal(t, date, DateValue(&date)) +} diff --git a/strfmt/conv/default.go b/strfmt/conv/default.go new file mode 100644 index 0000000..078a1e5 --- /dev/null +++ b/strfmt/conv/default.go @@ -0,0 +1,305 @@ +package conv + +import ( + "github.com/go-openapi/strfmt" +) + +// Base64 returns a pointer to of the Base64 value passed in. +func Base64(v strfmt.Base64) *strfmt.Base64 { + return &v +} + +// Base64Value returns the value of the Base64 pointer passed in or +// the default value if the pointer is nil. +func Base64Value(v *strfmt.Base64) strfmt.Base64 { + if v == nil { + return nil + } + + return *v +} + +// URI returns a pointer to of the URI value passed in. +func URI(v strfmt.URI) *strfmt.URI { + return &v +} + +// URIValue returns the value of the URI pointer passed in or +// the default value if the pointer is nil. +func URIValue(v *strfmt.URI) strfmt.URI { + if v == nil { + return strfmt.URI("") + } + + return *v +} + +// Email returns a pointer to of the Email value passed in. +func Email(v strfmt.Email) *strfmt.Email { + return &v +} + +// EmailValue returns the value of the Email pointer passed in or +// the default value if the pointer is nil. +func EmailValue(v *strfmt.Email) strfmt.Email { + if v == nil { + return strfmt.Email("") + } + + return *v +} + +// Hostname returns a pointer to of the Hostname value passed in. +func Hostname(v strfmt.Hostname) *strfmt.Hostname { + return &v +} + +// HostnameValue returns the value of the Hostname pointer passed in or +// the default value if the pointer is nil. +func HostnameValue(v *strfmt.Hostname) strfmt.Hostname { + if v == nil { + return strfmt.Hostname("") + } + + return *v +} + +// IPv4 returns a pointer to of the IPv4 value passed in. +func IPv4(v strfmt.IPv4) *strfmt.IPv4 { + return &v +} + +// IPv4Value returns the value of the IPv4 pointer passed in or +// the default value if the pointer is nil. +func IPv4Value(v *strfmt.IPv4) strfmt.IPv4 { + if v == nil { + return strfmt.IPv4("") + } + + return *v +} + +// IPv6 returns a pointer to of the IPv6 value passed in. +func IPv6(v strfmt.IPv6) *strfmt.IPv6 { + return &v +} + +// IPv6Value returns the value of the IPv6 pointer passed in or +// the default value if the pointer is nil. +func IPv6Value(v *strfmt.IPv6) strfmt.IPv6 { + if v == nil { + return strfmt.IPv6("") + } + + return *v +} + +// CIDR returns a pointer to of the CIDR value passed in. +func CIDR(v strfmt.CIDR) *strfmt.CIDR { + return &v +} + +// CIDRValue returns the value of the CIDR pointer passed in or +// the default value if the pointer is nil. +func CIDRValue(v *strfmt.CIDR) strfmt.CIDR { + if v == nil { + return strfmt.CIDR("") + } + + return *v +} + +// MAC returns a pointer to of the MAC value passed in. +func MAC(v strfmt.MAC) *strfmt.MAC { + return &v +} + +// MACValue returns the value of the MAC pointer passed in or +// the default value if the pointer is nil. +func MACValue(v *strfmt.MAC) strfmt.MAC { + if v == nil { + return strfmt.MAC("") + } + + return *v +} + +// UUID returns a pointer to of the UUID value passed in. +func UUID(v strfmt.UUID) *strfmt.UUID { + return &v +} + +// UUIDValue returns the value of the UUID pointer passed in or +// the default value if the pointer is nil. +func UUIDValue(v *strfmt.UUID) strfmt.UUID { + if v == nil { + return strfmt.UUID("") + } + + return *v +} + +// UUID3 returns a pointer to of the UUID3 value passed in. +func UUID3(v strfmt.UUID3) *strfmt.UUID3 { + return &v +} + +// UUID3Value returns the value of the UUID3 pointer passed in or +// the default value if the pointer is nil. +func UUID3Value(v *strfmt.UUID3) strfmt.UUID3 { + if v == nil { + return strfmt.UUID3("") + } + + return *v +} + +// UUID4 returns a pointer to of the UUID4 value passed in. +func UUID4(v strfmt.UUID4) *strfmt.UUID4 { + return &v +} + +// UUID4Value returns the value of the UUID4 pointer passed in or +// the default value if the pointer is nil. +func UUID4Value(v *strfmt.UUID4) strfmt.UUID4 { + if v == nil { + return strfmt.UUID4("") + } + + return *v +} + +// UUID5 returns a pointer to of the UUID5 value passed in. +func UUID5(v strfmt.UUID5) *strfmt.UUID5 { + return &v +} + +// UUID5Value returns the value of the UUID5 pointer passed in or +// the default value if the pointer is nil. +func UUID5Value(v *strfmt.UUID5) strfmt.UUID5 { + if v == nil { + return strfmt.UUID5("") + } + + return *v +} + +// ISBN returns a pointer to of the ISBN value passed in. +func ISBN(v strfmt.ISBN) *strfmt.ISBN { + return &v +} + +// ISBNValue returns the value of the ISBN pointer passed in or +// the default value if the pointer is nil. +func ISBNValue(v *strfmt.ISBN) strfmt.ISBN { + if v == nil { + return strfmt.ISBN("") + } + + return *v +} + +// ISBN10 returns a pointer to of the ISBN10 value passed in. +func ISBN10(v strfmt.ISBN10) *strfmt.ISBN10 { + return &v +} + +// ISBN10Value returns the value of the ISBN10 pointer passed in or +// the default value if the pointer is nil. +func ISBN10Value(v *strfmt.ISBN10) strfmt.ISBN10 { + if v == nil { + return strfmt.ISBN10("") + } + + return *v +} + +// ISBN13 returns a pointer to of the ISBN13 value passed in. +func ISBN13(v strfmt.ISBN13) *strfmt.ISBN13 { + return &v +} + +// ISBN13Value returns the value of the ISBN13 pointer passed in or +// the default value if the pointer is nil. +func ISBN13Value(v *strfmt.ISBN13) strfmt.ISBN13 { + if v == nil { + return strfmt.ISBN13("") + } + + return *v +} + +// CreditCard returns a pointer to of the CreditCard value passed in. +func CreditCard(v strfmt.CreditCard) *strfmt.CreditCard { + return &v +} + +// CreditCardValue returns the value of the CreditCard pointer passed in or +// the default value if the pointer is nil. +func CreditCardValue(v *strfmt.CreditCard) strfmt.CreditCard { + if v == nil { + return strfmt.CreditCard("") + } + + return *v +} + +// SSN returns a pointer to of the SSN value passed in. +func SSN(v strfmt.SSN) *strfmt.SSN { + return &v +} + +// SSNValue returns the value of the SSN pointer passed in or +// the default value if the pointer is nil. +func SSNValue(v *strfmt.SSN) strfmt.SSN { + if v == nil { + return strfmt.SSN("") + } + + return *v +} + +// HexColor returns a pointer to of the HexColor value passed in. +func HexColor(v strfmt.HexColor) *strfmt.HexColor { + return &v +} + +// HexColorValue returns the value of the HexColor pointer passed in or +// the default value if the pointer is nil. +func HexColorValue(v *strfmt.HexColor) strfmt.HexColor { + if v == nil { + return strfmt.HexColor("") + } + + return *v +} + +// RGBColor returns a pointer to of the RGBColor value passed in. +func RGBColor(v strfmt.RGBColor) *strfmt.RGBColor { + return &v +} + +// RGBColorValue returns the value of the RGBColor pointer passed in or +// the default value if the pointer is nil. +func RGBColorValue(v *strfmt.RGBColor) strfmt.RGBColor { + if v == nil { + return strfmt.RGBColor("") + } + + return *v +} + +// Password returns a pointer to of the Password value passed in. +func Password(v strfmt.Password) *strfmt.Password { + return &v +} + +// PasswordValue returns the value of the Password pointer passed in or +// the default value if the pointer is nil. +func PasswordValue(v *strfmt.Password) strfmt.Password { + if v == nil { + return strfmt.Password("") + } + + return *v +} diff --git a/strfmt/conv/default_test.go b/strfmt/conv/default_test.go new file mode 100644 index 0000000..8b779c5 --- /dev/null +++ b/strfmt/conv/default_test.go @@ -0,0 +1,129 @@ +package conv + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/go-openapi/strfmt" +) + +func TestBase64Value(t *testing.T) { + assert.Equal(t, strfmt.Base64(nil), Base64Value(nil)) + base64 := strfmt.Base64([]byte{4, 2}) + assert.Equal(t, base64, Base64Value(&base64)) +} + +func TestURIValue(t *testing.T) { + assert.Equal(t, strfmt.URI(""), URIValue(nil)) + value := strfmt.URI("foo") + assert.Equal(t, value, URIValue(&value)) +} + +func TestEmailValue(t *testing.T) { + assert.Equal(t, strfmt.Email(""), EmailValue(nil)) + value := strfmt.Email("foo") + assert.Equal(t, value, EmailValue(&value)) +} + +func TestHostnameValue(t *testing.T) { + assert.Equal(t, strfmt.Hostname(""), HostnameValue(nil)) + value := strfmt.Hostname("foo") + assert.Equal(t, value, HostnameValue(&value)) +} + +func TestIPv4Value(t *testing.T) { + assert.Equal(t, strfmt.IPv4(""), IPv4Value(nil)) + value := strfmt.IPv4("foo") + assert.Equal(t, value, IPv4Value(&value)) +} + +func TestIPv6Value(t *testing.T) { + assert.Equal(t, strfmt.IPv6(""), IPv6Value(nil)) + value := strfmt.IPv6("foo") + assert.Equal(t, value, IPv6Value(&value)) +} + +func TestCIDRValue(t *testing.T) { + assert.Equal(t, strfmt.CIDR(""), CIDRValue(nil)) + value := strfmt.CIDR("foo") + assert.Equal(t, value, CIDRValue(&value)) +} + +func TestMACValue(t *testing.T) { + assert.Equal(t, strfmt.MAC(""), MACValue(nil)) + value := strfmt.MAC("foo") + assert.Equal(t, value, MACValue(&value)) +} + +func TestUUIDValue(t *testing.T) { + assert.Equal(t, strfmt.UUID(""), UUIDValue(nil)) + value := strfmt.UUID("foo") + assert.Equal(t, value, UUIDValue(&value)) +} + +func TestUUID3Value(t *testing.T) { + assert.Equal(t, strfmt.UUID3(""), UUID3Value(nil)) + value := strfmt.UUID3("foo") + assert.Equal(t, value, UUID3Value(&value)) +} + +func TestUUID4Value(t *testing.T) { + assert.Equal(t, strfmt.UUID4(""), UUID4Value(nil)) + value := strfmt.UUID4("foo") + assert.Equal(t, value, UUID4Value(&value)) +} + +func TestUUID5Value(t *testing.T) { + assert.Equal(t, strfmt.UUID5(""), UUID5Value(nil)) + value := strfmt.UUID5("foo") + assert.Equal(t, value, UUID5Value(&value)) +} + +func TestISBNValue(t *testing.T) { + assert.Equal(t, strfmt.ISBN(""), ISBNValue(nil)) + value := strfmt.ISBN("foo") + assert.Equal(t, value, ISBNValue(&value)) +} + +func TestISBN10Value(t *testing.T) { + assert.Equal(t, strfmt.ISBN10(""), ISBN10Value(nil)) + value := strfmt.ISBN10("foo") + assert.Equal(t, value, ISBN10Value(&value)) +} + +func TestISBN13Value(t *testing.T) { + assert.Equal(t, strfmt.ISBN13(""), ISBN13Value(nil)) + value := strfmt.ISBN13("foo") + assert.Equal(t, value, ISBN13Value(&value)) +} + +func TestCreditCardValue(t *testing.T) { + assert.Equal(t, strfmt.CreditCard(""), CreditCardValue(nil)) + value := strfmt.CreditCard("foo") + assert.Equal(t, value, CreditCardValue(&value)) +} + +func TestSSNValue(t *testing.T) { + assert.Equal(t, strfmt.SSN(""), SSNValue(nil)) + value := strfmt.SSN("foo") + assert.Equal(t, value, SSNValue(&value)) +} + +func TestHexColorValue(t *testing.T) { + assert.Equal(t, strfmt.HexColor(""), HexColorValue(nil)) + value := strfmt.HexColor("foo") + assert.Equal(t, value, HexColorValue(&value)) +} + +func TestRGBColorValue(t *testing.T) { + assert.Equal(t, strfmt.RGBColor(""), RGBColorValue(nil)) + value := strfmt.RGBColor("foo") + assert.Equal(t, value, RGBColorValue(&value)) +} + +func TestPasswordValue(t *testing.T) { + assert.Equal(t, strfmt.Password(""), PasswordValue(nil)) + value := strfmt.Password("foo") + assert.Equal(t, value, PasswordValue(&value)) +} diff --git a/strfmt/conv/duration.go b/strfmt/conv/duration.go new file mode 100644 index 0000000..ea30132 --- /dev/null +++ b/strfmt/conv/duration.go @@ -0,0 +1,18 @@ +package conv + +import "github.com/go-openapi/strfmt" + +// Duration returns a pointer to of the Duration value passed in. +func Duration(v strfmt.Duration) *strfmt.Duration { + return &v +} + +// DurationValue returns the value of the Duration pointer passed in or +// the default value if the pointer is nil. +func DurationValue(v *strfmt.Duration) strfmt.Duration { + if v == nil { + return strfmt.Duration(0) + } + + return *v +} diff --git a/strfmt/conv/duration_test.go b/strfmt/conv/duration_test.go new file mode 100644 index 0000000..a015936 --- /dev/null +++ b/strfmt/conv/duration_test.go @@ -0,0 +1,15 @@ +package conv + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/go-openapi/strfmt" +) + +func TestDurationValue(t *testing.T) { + assert.Equal(t, strfmt.Duration(0), DurationValue(nil)) + duration := strfmt.Duration(42) + assert.Equal(t, duration, DurationValue(&duration)) +} diff --git a/strfmt/conv/time.go b/strfmt/conv/time.go new file mode 100644 index 0000000..627cd12 --- /dev/null +++ b/strfmt/conv/time.go @@ -0,0 +1,18 @@ +package conv + +import "github.com/go-openapi/strfmt" + +// DateTime returns a pointer to of the DateTime value passed in. +func DateTime(v strfmt.DateTime) *strfmt.DateTime { + return &v +} + +// DateTimeValue returns the value of the DateTime pointer passed in or +// the default value if the pointer is nil. +func DateTimeValue(v *strfmt.DateTime) strfmt.DateTime { + if v == nil { + return strfmt.DateTime{} + } + + return *v +} diff --git a/strfmt/conv/time_test.go b/strfmt/conv/time_test.go new file mode 100644 index 0000000..9196bdb --- /dev/null +++ b/strfmt/conv/time_test.go @@ -0,0 +1,16 @@ +package conv + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/go-openapi/strfmt" +) + +func TestDateTimeValue(t *testing.T) { + assert.Equal(t, strfmt.DateTime{}, DateTimeValue(nil)) + time := strfmt.DateTime(time.Now()) + assert.Equal(t, time, DateTimeValue(&time)) +} diff --git a/strfmt/conv/ulid.go b/strfmt/conv/ulid.go new file mode 100644 index 0000000..112bec3 --- /dev/null +++ b/strfmt/conv/ulid.go @@ -0,0 +1,18 @@ +package conv + +import "github.com/go-openapi/strfmt" + +// ULID returns a pointer to of the ULID value passed in. +func ULID(v strfmt.ULID) *strfmt.ULID { + return &v +} + +// ULIDValue returns the value of the ULID pointer passed in or +// the default value if the pointer is nil. +func ULIDValue(v *strfmt.ULID) strfmt.ULID { + if v == nil { + return strfmt.ULID{} + } + + return *v +} diff --git a/strfmt/conv/ulid_test.go b/strfmt/conv/ulid_test.go new file mode 100644 index 0000000..62525f9 --- /dev/null +++ b/strfmt/conv/ulid_test.go @@ -0,0 +1,22 @@ +package conv + +import ( + "testing" + + "github.com/go-openapi/strfmt" + "github.com/stretchr/testify/assert" +) + +const testUlid = string("01EYXZVGBHG26MFTG4JWR4K558") + +func TestULIDValue(t *testing.T) { + assert.Equal(t, strfmt.ULID{}, ULIDValue(nil)) + + value := strfmt.ULID{} + err := value.UnmarshalText([]byte(testUlid)) + assert.NoError(t, err) + assert.Equal(t, value, ULIDValue(&value)) + + ulidRef := ULID(value) + assert.Equal(t, &value, ulidRef) +} diff --git a/strfmt/date.go b/strfmt/date.go new file mode 100644 index 0000000..f0b3109 --- /dev/null +++ b/strfmt/date.go @@ -0,0 +1,187 @@ +// 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 strfmt + +import ( + "database/sql/driver" + "encoding/json" + "errors" + "fmt" + "time" + + "go.mongodb.org/mongo-driver/bson" +) + +func init() { + d := Date{} + // register this format in the default registry + Default.Add("date", &d, IsDate) +} + +// IsDate returns true when the string is a valid date +func IsDate(str string) bool { + _, err := time.Parse(RFC3339FullDate, str) + return err == nil +} + +const ( + // RFC3339FullDate represents a full-date as specified by RFC3339 + // See: http://goo.gl/xXOvVd + RFC3339FullDate = "2006-01-02" +) + +// Date represents a date from the API +// +// swagger:strfmt date +type Date time.Time + +// String converts this date into a string +func (d Date) String() string { + return time.Time(d).Format(RFC3339FullDate) +} + +// UnmarshalText parses a text representation into a date type +func (d *Date) UnmarshalText(text []byte) error { + if len(text) == 0 { + return nil + } + dd, err := time.Parse(RFC3339FullDate, string(text)) + if err != nil { + return err + } + *d = Date(dd) + return nil +} + +// MarshalText serializes this date type to string +func (d Date) MarshalText() ([]byte, error) { + return []byte(d.String()), nil +} + +// Scan scans a Date value from database driver type. +func (d *Date) Scan(raw interface{}) error { + switch v := raw.(type) { + case []byte: + return d.UnmarshalText(v) + case string: + return d.UnmarshalText([]byte(v)) + case time.Time: + *d = Date(v) + return nil + case nil: + *d = Date{} + return nil + default: + return fmt.Errorf("cannot sql.Scan() strfmt.Date from: %#v", v) + } +} + +// Value converts Date to a primitive value ready to written to a database. +func (d Date) Value() (driver.Value, error) { + return driver.Value(d.String()), nil +} + +// MarshalJSON returns the Date as JSON +func (d Date) MarshalJSON() ([]byte, error) { + return json.Marshal(time.Time(d).Format(RFC3339FullDate)) +} + +// UnmarshalJSON sets the Date from JSON +func (d *Date) UnmarshalJSON(data []byte) error { + if string(data) == jsonNull { + return nil + } + var strdate string + if err := json.Unmarshal(data, &strdate); err != nil { + return err + } + tt, err := time.Parse(RFC3339FullDate, strdate) + if err != nil { + return err + } + *d = Date(tt) + return nil +} + +func (d Date) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": d.String()}) +} + +func (d *Date) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if data, ok := m["data"].(string); ok { + rd, err := time.Parse(RFC3339FullDate, data) + if err != nil { + return err + } + *d = Date(rd) + return nil + } + + return errors.New("couldn't unmarshal bson bytes value as Date") +} + +// DeepCopyInto copies the receiver and writes its value into out. +func (d *Date) DeepCopyInto(out *Date) { + *out = *d +} + +// DeepCopy copies the receiver into a new Date. +func (d *Date) DeepCopy() *Date { + if d == nil { + return nil + } + out := new(Date) + d.DeepCopyInto(out) + return out +} + +// GobEncode implements the gob.GobEncoder interface. +func (d Date) GobEncode() ([]byte, error) { + return d.MarshalBinary() +} + +// GobDecode implements the gob.GobDecoder interface. +func (d *Date) GobDecode(data []byte) error { + return d.UnmarshalBinary(data) +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +func (d Date) MarshalBinary() ([]byte, error) { + return time.Time(d).MarshalBinary() +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +func (d *Date) UnmarshalBinary(data []byte) error { + var original time.Time + + err := original.UnmarshalBinary(data) + if err != nil { + return err + } + + *d = Date(original) + + return nil +} + +// Equal checks if two Date instances are equal +func (d Date) Equal(d2 Date) bool { + return time.Time(d).Equal(time.Time(d2)) +} diff --git a/strfmt/date_test.go b/strfmt/date_test.go new file mode 100644 index 0000000..7d86989 --- /dev/null +++ b/strfmt/date_test.go @@ -0,0 +1,175 @@ +// 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 strfmt + +import ( + "bytes" + "database/sql" + "database/sql/driver" + "encoding/gob" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/bson" +) + +var _ sql.Scanner = &Date{} +var _ driver.Valuer = Date{} + +func TestDate(t *testing.T) { + pp := Date{} + err := pp.UnmarshalText([]byte{}) + assert.NoError(t, err) + err = pp.UnmarshalText([]byte("yada")) + assert.Error(t, err) + + orig := "2014-12-15" + bj := []byte("\"" + orig + "\"") + err = pp.UnmarshalText([]byte(orig)) + assert.NoError(t, err) + + txt, err := pp.MarshalText() + assert.NoError(t, err) + assert.Equal(t, orig, string(txt)) + + err = pp.UnmarshalJSON(bj) + assert.NoError(t, err) + assert.EqualValues(t, orig, pp.String()) + + err = pp.UnmarshalJSON([]byte(`"1972/01/01"`)) + assert.Error(t, err) + + b, err := pp.MarshalJSON() + assert.NoError(t, err) + assert.Equal(t, bj, b) + + dateOriginal := Date(time.Date(2014, 10, 10, 0, 0, 0, 0, time.UTC)) + + bsonData, err := bson.Marshal(&dateOriginal) + assert.NoError(t, err) + + var dateCopy Date + err = bson.Unmarshal(bsonData, &dateCopy) + assert.NoError(t, err) + assert.Equal(t, dateOriginal, dateCopy) + + var dateZero Date + err = dateZero.UnmarshalJSON([]byte(jsonNull)) + assert.NoError(t, err) + assert.Equal(t, Date{}, dateZero) +} + +func TestDate_Scan(t *testing.T) { + ref := time.Now().Truncate(24 * time.Hour).UTC() + date, str := Date(ref), ref.Format(RFC3339FullDate) + + values := []interface{}{str, []byte(str), ref} + for _, value := range values { + result := Date{} + _ = (&result).Scan(value) + assert.Equal(t, date, result, "value: %#v", value) + } + + dd := Date{} + err := dd.Scan(nil) + assert.NoError(t, err) + assert.Equal(t, Date{}, dd) + + err = dd.Scan(19700101) + assert.Error(t, err) +} + +func TestDate_Value(t *testing.T) { + ref := time.Now().Truncate(24 * time.Hour).UTC() + date := Date(ref) + dbv, err := date.Value() + assert.NoError(t, err) + assert.EqualValues(t, dbv, ref.Format("2006-01-02")) +} + +func TestDate_IsDate(t *testing.T) { + tests := []struct { + value string + valid bool + }{ + {"2017-12-22", true}, + {"2017-1-1", false}, + {"17-13-22", false}, + {"2017-02-29", false}, // not a valid date : 2017 is not a leap year + {"1900-02-29", false}, // not a valid date : 1900 is not a leap year + {"2100-02-29", false}, // not a valid date : 2100 is not a leap year + {"2000-02-29", true}, // a valid date : 2000 is a leap year + {"2400-02-29", true}, // a valid date : 2000 is a leap year + {"2017-13-22", false}, + {"2017-12-32", false}, + {"20171-12-32", false}, + {"YYYY-MM-DD", false}, + {"20-17-2017", false}, + {"2017-12-22T01:02:03Z", false}, + } + for _, test := range tests { + assert.Equal(t, test.valid, IsDate(test.value), "value [%s] should be valid: [%t]", test.value, test.valid) + } +} + +func TestDeepCopyDate(t *testing.T) { + ref := time.Now().Truncate(24 * time.Hour).UTC() + date := Date(ref) + in := &date + + out := new(Date) + in.DeepCopyInto(out) + assert.Equal(t, in, out) + + out2 := in.DeepCopy() + assert.Equal(t, in, out2) + + var inNil *Date + out3 := inNil.DeepCopy() + assert.Nil(t, out3) +} + +func TestGobEncodingDate(t *testing.T) { + now := time.Now() + + b := bytes.Buffer{} + enc := gob.NewEncoder(&b) + err := enc.Encode(Date(now)) + assert.NoError(t, err) + assert.NotEmpty(t, b.Bytes()) + + var result Date + + dec := gob.NewDecoder(&b) + err = dec.Decode(&result) + assert.NoError(t, err) + assert.Equal(t, now.Year(), time.Time(result).Year()) + assert.Equal(t, now.Month(), time.Time(result).Month()) + assert.Equal(t, now.Day(), time.Time(result).Day()) +} + +func TestDate_Equal(t *testing.T) { + t.Parallel() + + d1 := Date(time.Date(2020, 10, 11, 12, 13, 14, 15, time.UTC)) + d2 := Date(time.Date(2020, 10, 11, 12, 13, 14, 15, time.UTC)) + d3 := Date(time.Date(2020, 11, 12, 13, 14, 15, 16, time.UTC)) + + //nolint:gocritic + assert.True(t, d1.Equal(d1), "Same Date should Equal itself") + assert.True(t, d1.Equal(d2), "Date instances should be equal") + assert.False(t, d1.Equal(d3), "Date instances should not be equal") +} diff --git a/strfmt/default.go b/strfmt/default.go new file mode 100644 index 0000000..a89a4de --- /dev/null +++ b/strfmt/default.go @@ -0,0 +1,2035 @@ +// 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 strfmt + +import ( + "database/sql/driver" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "net/mail" + "regexp" + "strings" + + "github.com/asaskevich/govalidator" + "go.mongodb.org/mongo-driver/bson" +) + +const ( + // HostnamePattern http://json-schema.org/latest/json-schema-validation.html#anchor114 + // A string instance is valid against this attribute if it is a valid + // representation for an Internet host name, as defined by RFC 1034, section 3.1 [RFC1034]. + // http://tools.ietf.org/html/rfc1034#section-3.5 + // ::= any one of the ten digits 0 through 9 + // var digit = /[0-9]/; + // ::= any one of the 52 alphabetic characters A through Z in upper case and a through z in lower case + // var letter = /[a-zA-Z]/; + // ::= | + // var letDig = /[0-9a-zA-Z]/; + // ::= | "-" + // var letDigHyp = /[-0-9a-zA-Z]/; + // ::= | + // var ldhStr = /[-0-9a-zA-Z]+/; + //