16 Commits

Author SHA1 Message Date
Colin Henry
086c404610 changed parameter identifier to brackets over a colon, is more compatable with OpenAPI 2022-08-20 13:43:39 -07:00
Colin Henry
f9a712bc33 added to the logger interface 2022-08-05 22:04:20 -07:00
Colin Henry
dc5f8b6e5a new ServeMux based on Mat Ryer's Way. removed method support in the mux to allow it to be handled by the handlers themselves. Keeps the standard library net/http handler contract and provides better support for the handlers provided by x's net/http. 2022-07-08 19:32:45 -07:00
Colin Henry
62911f042d update library to build for 1.18 2022-06-28 20:23:08 -07:00
Colin Henry
31cb99467c fix incorrect return code 2020-12-10 09:25:23 -08:00
Colin Henry
8a1403a35d nitpicks. 2020-10-23 21:55:48 -07:00
Colin Henry
07db4cf999 dont make it directional 2020-09-04 20:24:37 -07:00
Colin Henry
4b8fd0b78d added direction to Actor to prevent confusion 2020-08-22 18:45:15 -07:00
Colin Henry
ad5ed58296 overhaul of the rest package, made it a little bit more testable(WIP) and understandable 2020-08-22 17:00:38 -07:00
Colin Henry
d4a7732535 overhaul of the rest package, made it a little bit more testable(WIP) and understandable 2020-08-22 16:55:32 -07:00
Colin Henry
4fe5a53450 added support for new logger in rest.Collection 2020-08-22 15:54:49 -07:00
Colin Henry
ed136ca83e new logging package, just the essentials 2020-08-22 15:47:37 -07:00
Colin Henry
284c8f8359 removing poluting interface parameter 2020-08-22 15:33:18 -07:00
Colin Henry
15ef3556ba makeing BasicAuth a little more generic and usable 2020-08-18 19:18:03 -07:00
Colin Henry
85df14e0c6 commented code cleanup 2020-08-18 17:47:14 -07:00
Colin Henry
37d0d5e98d removed unnecessary sums 2020-08-18 17:22:45 -07:00
16 changed files with 446 additions and 201 deletions

10
.drone.yml Normal file
View File

@@ -0,0 +1,10 @@
kind: pipeline
type: docker
name: default
steps:
- name: test
image: golang
commands:
- go test ./...
- go build ./...

View File

@@ -2,6 +2,8 @@
A mix of useful packages, some are works in progress.
[![Build Status](https://ci.j5y.xyz/api/badges/jchenry/x/status.svg)](https://ci.j5y.xyz/jchenry/x)
## Install
```

View File

@@ -12,12 +12,3 @@ func Encoder(w io.Writer, e interface{}) error {
func Decoder(r io.Reader, e interface{}) error {
return json.NewDecoder(r).Decode(e)
}
// func Decoder(get func() interface{}) func(io.Reader) (interface{}, error) {
// //TODO I dont like the get() function, find a better way of dealing with this
// return func(r io.Reader) (interface{}, error) {
// e := get()
// err := json.NewDecoder(r).Decode(e)
// return e, err
// }
// }

View File

@@ -12,13 +12,3 @@ func Encoder(w io.Writer, e interface{}) error {
func Decoder(r io.Reader, e interface{}) error {
return xml.NewDecoder(r).Decode(e)
}
// func Decoder(get func() interface{}) func(io.Reader) (interface{}, error) {
// //TODO I dont like the get() function, find a better way of dealing with this
// return func(r io.Reader) (interface{}, error) {
// e := get()
// err := xml.NewDecoder(r).Decode(e)
// return e, err
// }
// }
//

2
go.mod
View File

@@ -1,3 +1,3 @@
module github.com/jchenry/x
go 1.13
go 1.18

136
go.sum
View File

@@ -1,136 +0,0 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
fyne.io/fyne v1.3.0 h1:FLlgX/JkD3Chal7tEhRL7fOONVAjQJM/yrVNA+cK/dc=
fyne.io/fyne v1.3.0/go.mod h1:AcBUeR8hetITnnfaLvuVqioWM/lT18WPeMVAobhMbg8=
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
github.com/PuerkitoBio/rehttp v0.0.0-20180310210549-11cf6ea5d3e9 h1:VE0eMvNSQI72dADsq4gm5KpNPmt97WgqneTfaS5MWrs=
github.com/PuerkitoBio/rehttp v0.0.0-20180310210549-11cf6ea5d3e9/go.mod h1:ItsOiHl4XeMOV3rzbZqQRjLc3QQxbE6391/9iNG7rE8=
github.com/PuloV/ics-golang v0.0.0-20190808201353-a3394d3bcade h1:odEkSCl2gLWPtvraEdCyBZbeYyMMTysWPLMurnB8sUY=
github.com/PuloV/ics-golang v0.0.0-20190808201353-a3394d3bcade/go.mod h1:f1P3hjG+t54/IrnXMnnw+gRmFCDR/ryj9xSQ7MPMkQw=
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/channelmeter/iso8601duration v0.0.0-20150204201828-8da3af7a2a61 h1:o64h9XF42kVEUuhuer2ehqrlX8rZmvQSU0+Vpj1rF6Q=
github.com/channelmeter/iso8601duration v0.0.0-20150204201828-8da3af7a2a61/go.mod h1:Rp8e0DCtEKwXFOC6JPJQVTz8tuGoGvw6Xfexggh/ed0=
github.com/codegangsta/negroni v1.0.0 h1:+aYywywx4bnKXWvoWtRfJ91vC59NbEhEY03sZjQhbVY=
github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0=
github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM=
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
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/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fyne-io/mobile v0.0.2 h1:eGmCR5lkFxk0PnPafGppLFRD5QODJfSVdrjhLjanOVg=
github.com/fyne-io/mobile v0.0.2/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY=
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw=
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8=
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ=
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
github.com/jchenry/jchenry v0.0.0-20200615172632-cb0bc37e6b16 h1:vVomaWqIbI/Vyb6uGE8ANmF8V3ktoLiOXdcKQLvwUc4=
github.com/jchenry/jchenry v0.0.0-20200615172632-cb0bc37e6b16/go.mod h1:WLNY6BKAzrUIfnkPA8WCUxkKchKZss4fRSVmbKZuhMg=
github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE=
github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
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/lucor/goinfo v0.0.0-20200401173949-526b5363a13a/go.mod h1:ORP3/rB5IsulLEBwQZCJyyV6niqmI7P4EWSmkug+1Ng=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/rsc/rsc v0.0.0-20180427141835-fc6202590229 h1:s5M0EEh5JyTx0PrhLGlog+CegHIkmiCd07ht20coRtA=
github.com/rsc/rsc v0.0.0-20180427141835-fc6202590229/go.mod h1:TJRSe/n0/H37q9TsEwBtcOz32UX+UWqgapLwsXTV4jE=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 h1:HunZiaEKNGVdhTRQOVpMmj5MQnGnv+e8uZNu3xFLgyM=
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 h1:m59mIOBO4kfcNCEzJNy71UkeF4XIx2EVmL9KLwDQdmM=
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stripe/stripe-go v63.4.0+incompatible h1:zzZR004GZ/si7nyckn4NBhoQOViUu5VJ/sA7NT7oTSs=
github.com/stripe/stripe-go v63.4.0+incompatible/go.mod h1:A1dQZmO/QypXmsL0T8axYZkSN/uA/T/A64pfKdBAMiY=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad h1:5E5raQxcv+6CZ11RrBYQe5WRbUIWpScjh0kvHZkZIrQ=
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 h1:TC0v2RSO1u2kn1ZugjrFXkRZAEaqMN/RW+OTZkBzmLE=
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190808195139-e713427fea3f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200328031815-3db5fc6bac03/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/auth0.v1 v1.2.7 h1:9UCE5rKFL60rqQENmmJaGdNu7/aby8r8wVcJ83Vj5oU=
gopkg.in/auth0.v1 v1.2.7/go.mod h1:1FRtMXwYDgygZcO7Of7kj/I4mf9UjHGhMHUOqNT0d0M=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
rsc.io/dbstore v0.1.1 h1:LI4gBJUwbejn0wHJWe0KTwgCM33zUVP3BsNz5y2fkEE=
rsc.io/dbstore v0.1.1/go.mod h1:zI7k1PCSLg9r/T2rBM4E/SctbGmqdtt3kjQSemVh1Rs=
rsc.io/rsc v0.0.0-20180427141835-fc6202590229 h1:6s5zUknxnRp4D3GlNb7uDzlcfFVq9G2ficO+k4Bcb6w=
rsc.io/rsc v0.0.0-20180427141835-fc6202590229/go.mod h1:nHU4RAWoD9u1Hr+vTW0mktVbANmwCPkTwT2xNpVs/70=
rsc.io/sqlite v0.5.0 h1:HG63YxeP0eALjqorwnJ9ENxUUOUR6NYJ4FHEKFJ7aVk=
rsc.io/sqlite v0.5.0/go.mod h1:fqHuveM9iIqMzjD0WiZIvKYMty/WqTo2bxE9+zC54WE=

14
log/logger.go Normal file
View File

@@ -0,0 +1,14 @@
package log
// Logger is a logging interface with only the essentials that a function that needs to log should care about. Compatible with standard Go logger.
type Logger interface {
Fatal(v ...any)
Fatalf(format string, v ...any)
Fatalln(v ...any)
Panic(v ...any)
Panicf(format string, v ...any)
Panicln(v ...any)
Print(v ...any)
Printf(format string, v ...any)
Println(v ...any)
}

14
log/none.go Normal file
View File

@@ -0,0 +1,14 @@
package log
// None provides a logger that doesnt log anything
type None struct{}
func (n None) Fatal(v ...any) {}
func (n None) Fatalf(format string, v ...any) {}
func (n None) Fatalln(v ...any) {}
func (n None) Panic(v ...any) {}
func (n None) Panicf(format string, v ...any) {}
func (n None) Panicln(v ...any) {}
func (n None) Print(v ...any) {}
func (n None) Printf(format string, v ...any) {}
func (n None) Println(v ...any) {}

View File

@@ -1,19 +1,28 @@
package http
import (
"crypto/sha1"
"encoding/base64"
"fmt"
"net/http"
"os"
"strings"
)
func BasicAuth(h http.Handler) http.HandlerFunc {
func BasicAuth(h http.Handler, htpasswd map[string]string, realm string) http.HandlerFunc {
rlm := fmt.Sprintf(`Basic realm="%s"`, realm)
sha1 := func(password string) string {
s := sha1.New()
_, _ = s.Write([]byte(password))
passwordSum := []byte(s.Sum(nil))
return base64.StdEncoding.EncodeToString(passwordSum)
}
return func(w http.ResponseWriter, r *http.Request) {
user, pass, _ := r.BasicAuth()
if !(user == os.Getenv("WIKI_USERNAME") && pass == os.Getenv("WIKI_PASSWORD")) {
w.Header().Set("WWW-Authenticate", `Basic realm="wiki"`)
http.Error(w, "Unauthorized.", 401)
if pw, ok := htpasswd[user]; !ok || !strings.EqualFold(pass, sha1(pw)) {
w.Header().Set("WWW-Authenticate", rlm)
http.Error(w, "Unauthorized", 401)
return
}
h.ServeHTTP(w, r)
}
}

View File

@@ -30,7 +30,7 @@ func MutliHandler(h map[string]http.Handler) (http.HandlerFunc, error) {
if hdlr, ok := h[r.Method]; ok {
hdlr.ServeHTTP(w, r)
} else {
NotFoundHandler.ServeHTTP(w, r)
NotAllowedHandler.ServeHTTP(w, r)
}
}, nil
}

106
net/http/mux.go Normal file
View File

@@ -0,0 +1,106 @@
package http
import (
"context"
"net/http"
"strings"
)
// wayContextKey is the context key type for storing
// parameters in context.Context.
type wayContextKey string
// Router routes HTTP requests.
type ServeMux struct {
routes []*route
// NotFound is the http.Handler to call when no routes
// match. By default uses http.NotFoundHandler().
NotFound http.Handler
}
// NewRouter makes a new Router.
func NewServeMux() *ServeMux {
return &ServeMux{
NotFound: http.NotFoundHandler(),
}
}
func (r *ServeMux) pathSegments(p string) []string {
return strings.Split(strings.Trim(p, "/"), "/")
}
// Handle adds a handler with the specified pattern.
// Pattern can contain path segments such as: /item/:id which is
// accessible via the Param function.
// If pattern ends with trailing /, it acts as a prefix.
func (r *ServeMux) Handle(pattern string, handler http.Handler) {
route := &route{
segs: r.pathSegments(pattern),
handler: handler,
prefix: strings.HasSuffix(pattern, "/") || strings.HasSuffix(pattern, "..."),
}
r.routes = append(r.routes, route)
}
// HandleFunc is the http.HandlerFunc alternative to http.Handle.
func (r *ServeMux) HandleFunc(pattern string, fn http.HandlerFunc) {
r.Handle(pattern, fn)
}
// ServeHTTP routes the incoming http.Request based on path
func (r *ServeMux) ServeHTTP(w http.ResponseWriter, req *http.Request) {
segs := r.pathSegments(req.URL.Path)
for _, route := range r.routes {
if ctx, ok := route.match(req.Context(), r, segs); ok {
route.handler.ServeHTTP(w, req.WithContext(ctx))
return
}
}
r.NotFound.ServeHTTP(w, req)
}
// Param gets the path parameter from the specified Context.
// Returns an empty string if the parameter was not found.
func Param(ctx context.Context, param string) string {
vStr, ok := ctx.Value(wayContextKey(param)).(string)
if !ok {
return ""
}
return vStr
}
type route struct {
segs []string
handler http.Handler
prefix bool
}
func (r *route) match(ctx context.Context, router *ServeMux, segs []string) (context.Context, bool) {
if len(segs) > len(r.segs) && !r.prefix {
return nil, false
}
for i, seg := range r.segs {
if i > len(segs)-1 {
return nil, false
}
isParam := false
if strings.HasPrefix(seg, "{") {
isParam = true
seg = strings.Trim(seg, "{}")
}
if !isParam { // verbatim check
if strings.HasSuffix(seg, "...") {
if strings.HasPrefix(segs[i], seg[:len(seg)-3]) {
return ctx, true
}
}
if seg != segs[i] {
return nil, false
}
}
if isParam {
ctx = context.WithValue(ctx, wayContextKey(seg), segs[i])
}
}
return ctx, true
}

231
net/http/mux_test.go Normal file
View File

@@ -0,0 +1,231 @@
package http
import (
"context"
"net/http"
"net/http/httptest"
"testing"
)
var tests = []struct {
// RouteMethod string
RoutePattern string
Method string
Path string
Match bool
Params map[string]string
}{
// simple path matching
{
"/one",
"GET", "/one", true, nil,
},
{
"/two",
"GET", "/two", true, nil,
},
{
"/three",
"GET", "/three", true, nil,
},
// methods
{
"/methodcase",
"GET", "/methodcase", true, nil,
},
{
"/methodcase",
"get", "/methodcase", true, nil,
},
{
"/methodcase",
"get", "/methodcase", true, nil,
},
{
"/method1",
"POST", "/method1", true, nil,
},
{
"/method2",
"GET", "/method2", true, nil,
},
{
"/method3",
"PUT", "/method3", true, nil,
},
// all methods
{
"/all-methods",
"GET", "/all-methods", true, nil,
},
{
"/all-methods",
"POST", "/all-methods", true, nil,
},
{
"/all-methods",
"PUT", "/all-methods", true, nil,
},
// nested
{
"/parent/child/one",
"GET", "/parent/child/one", true, nil,
},
{
"/parent/child/two",
"GET", "/parent/child/two", true, nil,
},
{
"/parent/child/three",
"GET", "/parent/child/three", true, nil,
},
// slashes
{
"slashes/one",
"GET", "/slashes/one", true, nil,
},
{
"/slashes/two",
"GET", "slashes/two", true, nil,
},
{
"slashes/three/",
"GET", "/slashes/three", true, nil,
},
{
"/slashes/four",
"GET", "slashes/four/", true, nil,
},
// prefix
{
"/prefix/",
"GET", "/prefix/anything/else", true, nil,
},
{
"/not-prefix",
"GET", "/not-prefix/anything/else", false, nil,
},
{
"/prefixdots...",
"GET", "/prefixdots/anything/else", true, nil,
},
{
"/prefixdots...",
"GET", "/prefixdots", true, nil,
},
// path params
{
"/path-param/{id}",
"GET", "/path-param/123", true, map[string]string{"id": "123"},
},
{
"/path-params/{era}/{group}/{member}",
"GET", "/path-params/60s/beatles/lennon", true, map[string]string{
"era": "60s",
"group": "beatles",
"member": "lennon",
},
},
{
"/path-params-prefix/{era}/{group}/{member}/",
"GET", "/path-params-prefix/60s/beatles/lennon/yoko", true, map[string]string{
"era": "60s",
"group": "beatles",
"member": "lennon",
},
},
// misc no matches
{
"/not/enough",
"GET", "/not/enough/items", false, nil,
},
{
"/not/enough/items",
"GET", "/not/enough", false, nil,
},
}
func TestWay(t *testing.T) {
for _, test := range tests {
r := NewServeMux()
match := false
var ctx context.Context
r.Handle(test.RoutePattern, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
match = true
ctx = r.Context()
}))
req, err := http.NewRequest(test.Method, test.Path, nil)
if err != nil {
t.Errorf("NewRequest: %s", err)
}
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if match != test.Match {
t.Errorf("expected match %v but was %v: %s %s", test.Match, match, test.Method, test.Path)
}
if len(test.Params) > 0 {
for expK, expV := range test.Params {
// check using helper
actualValStr := Param(ctx, expK)
if actualValStr != expV {
t.Errorf("Param: context value %s expected \"%s\" but was \"%s\"", expK, expV, actualValStr)
}
}
}
}
}
func TestMultipleRoutesDifferentMethods(t *testing.T) {
r := NewServeMux()
var match string
r.Handle("/route", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
match = "GET /route"
case http.MethodDelete:
match = "DELETE /route"
case http.MethodPost:
match = "POST /route"
}
}))
r.Handle("/route", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
match = "GET /route"
}))
r.Handle("/route", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
match = "DELETE /route"
}))
r.Handle("/route", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
match = "POST /route"
}))
req, err := http.NewRequest(http.MethodGet, "/route", nil)
if err != nil {
t.Errorf("NewRequest: %s", err)
}
r.ServeHTTP(httptest.NewRecorder(), req)
if match != "GET /route" {
t.Errorf("unexpected: %s", match)
}
req, err = http.NewRequest(http.MethodDelete, "/route", nil)
if err != nil {
t.Errorf("NewRequest: %s", err)
}
r.ServeHTTP(httptest.NewRecorder(), req)
if match != "DELETE /route" {
t.Errorf("unexpected: %s", match)
}
req, err = http.NewRequest(http.MethodPost, "/route", nil)
if err != nil {
t.Errorf("NewRequest: %s", err)
}
r.ServeHTTP(httptest.NewRecorder(), req)
if match != "POST /route" {
t.Errorf("unexpected: %s", match)
}
}

View File

@@ -11,11 +11,12 @@ type StatusHandler int
func (s StatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
code := int(s)
w.WriteHeader(code)
io.WriteString(w, http.StatusText(code))
_, _ = io.WriteString(w, http.StatusText(code))
}
var (
NotFoundHandler = StatusHandler(404)
NotImplementedHandler = StatusHandler(501)
NotLegalHandler = StatusHandler(451)
NotFoundHandler = StatusHandler(http.StatusNotFound)
NotImplementedHandler = StatusHandler(http.StatusNotImplemented)
NotLegalHandler = StatusHandler(http.StatusUnavailableForLegalReasons)
NotAllowedHandler = StatusHandler(http.StatusMethodNotAllowed)
)

View File

@@ -7,64 +7,53 @@ import (
"sync"
"github.com/jchenry/x/encoding"
"github.com/jchenry/x/log"
)
type CollectionStore interface {
All(params url.Values) (interface{}, error)
Get(id string) (interface{}, error)
Delete(id string) error
Update(e interface{}) error
New(e interface{}) error
}
// Example: Collection(p, c, JSONEncoder, json.Decode(func()interface{}{return &foo{}}))
// type Decoder func(io.Reader) (interface{}, error)
func Collection(pool *sync.Pool, store CollectionStore, encode EntityEncoder, decode encoding.Decoder) http.HandlerFunc {
return EntityHandler(
collectionGet(store, encode),
collectionPost(store, encode, decode, pool),
collectionPut(store, encode, decode, pool),
collectionDelete(store, encode),
// Example: Resource(p, c, JSONEncoder, json.Decode(func()interface{}{return &foo{}}), log.None{})
func Resource(p *sync.Pool, g Gateway, e EntityEncoder, d encoding.Decoder, l log.Logger) http.HandlerFunc {
return restVerbHandler(
GetResource(g, e, l),
PostResource(g, d, p, l),
PutResource(g, e, d, p, l),
DeleteResource(g, l),
)
}
func collectionGet(store CollectionStore, encode EntityEncoder) http.HandlerFunc {
func GetResource(store Readable, encode EntityEncoder, log log.Logger) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { // GET
if id := filepath.Base(r.URL.Path); id != "" {
if e, err := store.Get(id); err == nil { // handle individual entity
if e, err := store.Read(id); err == nil { // handle individual entity
encode(w, e)
} else {
w.WriteHeader(http.StatusInternalServerError)
encode(w, err)
log.Printf("Error: %s", err)
}
} else {
if params, err := url.ParseQuery(r.URL.RawQuery); err == nil {
if e, err := store.All(params); err == nil { // handle all entities
encode(w, e)
} else {
// TODO: we really should write a header here, but need to figure out what it should be
w.WriteHeader(http.StatusInternalServerError)
log.Printf("Error: %s", err)
}
} else {
// encode(w, err)
w.WriteHeader(http.StatusBadRequest)
}
}
}
}
func collectionPost(store CollectionStore, encode EntityEncoder, decode encoding.Decoder, pool *sync.Pool) http.HandlerFunc {
func PostResource(store Creatable, decode encoding.Decoder, pool *sync.Pool, log log.Logger) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { // POST TODO
e := pool.New()
e := pool.Get()
defer pool.Put(e)
if err := decode(r.Body, e); err == nil {
if err = store.New(e); err == nil {
if err = store.Create(e); err == nil {
w.WriteHeader(http.StatusCreated)
} else {
w.WriteHeader(http.StatusInternalServerError)
log.Printf("Error: %s", err)
}
} else {
w.WriteHeader(http.StatusBadRequest)
@@ -72,9 +61,9 @@ func collectionPost(store CollectionStore, encode EntityEncoder, decode encoding
}
}
func collectionPut(store CollectionStore, encode EntityEncoder, decode encoding.Decoder, pool *sync.Pool) http.HandlerFunc {
func PutResource(store Updatable, encode EntityEncoder, decode encoding.Decoder, pool *sync.Pool, log log.Logger) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { // PUT TODO
e := pool.New()
e := pool.Get()
defer pool.Put(e)
if err := decode(r.Body, e); err == nil {
if err = store.Update(e); err == nil {
@@ -82,23 +71,22 @@ func collectionPut(store CollectionStore, encode EntityEncoder, decode encoding.
encode(w, e)
} else {
w.WriteHeader(http.StatusInternalServerError)
encode(w, err)
log.Printf("Error: %s", err)
}
} else {
w.WriteHeader(http.StatusBadRequest)
encode(w, err)
}
}
}
func collectionDelete(store CollectionStore, encode EntityEncoder) http.HandlerFunc {
func DeleteResource(store Deletable, log log.Logger) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { // DELETE TODO
if id := filepath.Base(r.URL.Path); id != "" {
if err := store.Delete(id); err == nil {
w.WriteHeader(http.StatusNoContent)
} else {
w.WriteHeader(http.StatusInternalServerError)
log.Printf("Error: %s", err)
}
} else {
w.WriteHeader(http.StatusBadRequest)

View File

@@ -7,7 +7,7 @@ import (
)
// EntityHandler returns a handler that provides restful verbs, following a CRUD model
func EntityHandler(
func restVerbHandler(
get gohttp.Handler,
post gohttp.Handler,
put gohttp.Handler,

25
rest/gateway.go Normal file
View File

@@ -0,0 +1,25 @@
package rest
type Creatable interface {
Create(e interface{}) error
}
type Updatable interface {
Update(e interface{}) error
}
type Deletable interface {
Delete(id string) error
}
type Readable interface {
All(filters map[string][]string) (interface{}, error)
Read(id string) (interface{}, error)
}
type Gateway interface {
Creatable
Updatable
Deletable
Readable
}