From ab026ef471fef6b28a3c20590a6a40da52d754d4 Mon Sep 17 00:00:00 2001 From: James Tombleson Date: Tue, 23 Apr 2024 22:18:07 -0700 Subject: [PATCH] chi was replaced with echo --- go.mod | 25 +- go.sum | 57 +++-- internal/domain/requests.go | 13 + internal/domain/responses.go | 5 + internal/handler/v1/articles.go | 109 ++++----- internal/handler/v1/discordwebhooks.go | 182 +++++++------- internal/handler/v1/handler.go | 151 ++++++++++++ internal/handler/v1/queue.go | 22 +- internal/handler/v1/server.go | 127 ---------- internal/handler/v1/settings.go | 39 ++- internal/handler/v1/sources.go | 324 ++++++++++++++----------- internal/handler/v1/subscriptions.go | 125 ++++------ 12 files changed, 618 insertions(+), 561 deletions(-) create mode 100644 internal/domain/requests.go create mode 100644 internal/domain/responses.go create mode 100644 internal/handler/v1/handler.go delete mode 100644 internal/handler/v1/server.go diff --git a/go.mod b/go.mod index 1ba1234..511e25a 100644 --- a/go.mod +++ b/go.mod @@ -8,11 +8,23 @@ require ( github.com/go-rod/rod v0.107.1 github.com/google/uuid v1.3.0 github.com/joho/godotenv v1.4.0 + github.com/labstack/echo/v4 v4.12.0 github.com/mmcdole/gofeed v1.1.3 github.com/nicklaw5/helix/v2 v2.4.0 github.com/robfig/cron/v3 v3.0.1 - github.com/swaggo/http-swagger v1.3.0 - github.com/swaggo/swag v1.8.2 + github.com/swaggo/echo-swagger v1.4.1 + github.com/swaggo/swag v1.8.12 +) + +require ( + github.com/ghodss/yaml v1.0.0 // indirect + github.com/labstack/gommon v0.4.2 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/swaggo/files/v2 v2.0.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + golang.org/x/crypto v0.22.0 // indirect ) require ( @@ -30,13 +42,12 @@ require ( github.com/mmcdole/goxpp v0.0.0-20200921145534-2f3784f67354 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe // indirect github.com/ysmood/goob v0.4.0 // indirect github.com/ysmood/gson v0.7.2 // indirect github.com/ysmood/leakless v0.7.0 // indirect - golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect - golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098 // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/tools v0.1.11 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.7.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 99d5be8..5b3b982 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 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/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -44,6 +46,10 @@ 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/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= +github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= +github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -51,6 +57,11 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN 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.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mmcdole/gofeed v1.1.3 h1:pdrvMb18jMSLidGp8j0pLvc9IGziX4vbmvVqmLH6z8o= github.com/mmcdole/gofeed v1.1.3/go.mod h1:QQO3maftbOu+hiVOGOZDRLymqGQCos4zxbA4j89gMrE= github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf/go.mod h1:pasqhqstspkosTneA62Nc+2p9SOBBYAPbnmRRWPQ0V8= @@ -75,14 +86,19 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe h1:K8pHPVoTgxFJt1lXuIzzOX7zZhZFldJQK/CgKx9BFIc= -github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= -github.com/swaggo/http-swagger v1.3.0 h1:1+6M4qRorIbdyTWTsGrwnb0r9jGK5dcWN82O6oY/yHQ= -github.com/swaggo/http-swagger v1.3.0/go.mod h1:9glekdg40lwclrrKNRGgj/IMDxpNPZ3kzab4oPcF8EM= -github.com/swaggo/swag v1.8.2 h1:D4aBiVS2a65zhyk3WFqOUz7Rz0sOaUcgeErcid5uGL4= -github.com/swaggo/swag v1.8.2/go.mod h1:jMLeXOOmYyjk8PvHTsXBdrubsNd9gUJTTCzL5iBnseg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/swaggo/echo-swagger v1.4.1 h1:Yf0uPaJWp1uRtDloZALyLnvdBeoEL5Kc7DtnjzO/TUk= +github.com/swaggo/echo-swagger v1.4.1/go.mod h1:C8bSi+9yH2FLZsnhqMZLIZddpUxZdBYuNHbtaS1Hljc= +github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw= +github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM= +github.com/swaggo/swag v1.8.12 h1:pctzkNPu0AlQP2royqX3apjKCQonAnf7KGoxeO4y64w= +github.com/swaggo/swag v1.8.12/go.mod h1:lNfm6Gg+oAq3zRJQNEMBE66LIJKM44mxFqhEEgy2its= github.com/urfave/cli v1.22.3/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ= github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18= github.com/ysmood/got v0.29.5 h1:+wMnm8UjoyYFMfeAsr57a1bahWTkloysc0Hxsu2gmnM= @@ -95,28 +111,32 @@ github.com/ysmood/gson v0.7.2/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3R github.com/ysmood/leakless v0.7.0 h1:XCGdaPExyoreoQd+H5qgxM3ReNbSPFsEXpSKwbXbwQw= github.com/ysmood/leakless v0.7.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098 h1:PgOr27OhUx2IRqGJ2RxAWI4dJQ7bi9cSrB82uzFzfUA= -golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 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/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY= -golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= 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 h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= @@ -125,5 +145,6 @@ 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-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/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/internal/domain/requests.go b/internal/domain/requests.go new file mode 100644 index 0000000..b7bd1ad --- /dev/null +++ b/internal/domain/requests.go @@ -0,0 +1,13 @@ +package domain + +type GetSourceBySourceAndNameParamRequest struct { + Name string `query:"name"` + Source string `query:"source"` +} + +type NewSourceParamRequest struct { + Name string `query:"name"` + Url string `query:"url"` + Tags string `query:"tags"` +} + diff --git a/internal/domain/responses.go b/internal/domain/responses.go new file mode 100644 index 0000000..b4805a6 --- /dev/null +++ b/internal/domain/responses.go @@ -0,0 +1,5 @@ +package domain + +type ErrorResponse struct { + Message string `json:"message"` +} \ No newline at end of file diff --git a/internal/handler/v1/articles.go b/internal/handler/v1/articles.go index cc9968a..5703404 100644 --- a/internal/handler/v1/articles.go +++ b/internal/handler/v1/articles.go @@ -5,22 +5,22 @@ import ( "strconv" "git.jamestombleson.com/jtom38/newsbot-api/internal/domain/models" - "github.com/go-chi/chi/v5" "github.com/google/uuid" + "github.com/labstack/echo/v4" ) -func (s *Server) GetArticleRouter() http.Handler { - r := chi.NewRouter() - - r.Get("/", s.listArticles) - r.Route("/{ID}", func(r chi.Router) { - r.Get("/", s.getArticle) - r.Get("/details", s.getArticleDetails) - }) - r.Get("/by/sourceid", s.ListArticlesBySourceId) - - return r -} +//func (s *Handler) GetArticleRouter() http.Handler { +// r := chi.NewRouter() +// +// r.Get("/", s.listArticles) +// r.Route("/{ID}", func(r chi.Router) { +// r.Get("/", s.getArticle) +// r.Get("/details", s.getArticleDetails) +// }) +// r.Get("/by/sourceid", s.ListArticlesBySourceId) +// +// return r +//} type ArticlesListResults struct { ApiStatusModel @@ -44,7 +44,7 @@ type ArticleDetailsResult struct { // @Tags Articles // @Router /articles [get] // @Success 200 {object} ArticlesListResults "OK" -func (s *Server) listArticles(w http.ResponseWriter, r *http.Request) { +func (s *Handler) listArticles(c echo.Context) error { p := ArticlesListResults{ ApiStatusModel: ApiStatusModel{ Message: "OK", @@ -52,32 +52,28 @@ func (s *Server) listArticles(w http.ResponseWriter, r *http.Request) { }, } - query := r.URL.Query() - queryPage := query["page"] + queryPage := c.QueryParam("page") // if a page number was sent, process it if len(queryPage) >= 1 { - page, err := strconv.Atoi(query["page"][0]) + page, err := strconv.Atoi(queryPage) if err != nil { - s.WriteError(w, err.Error(), http.StatusBadRequest) - return + return c.JSON(http.StatusBadRequest, err) } - res, err := s.dto.ListArticles(r.Context(), 25, page) + res, err := s.dto.ListArticles(c.Request().Context(), 25, page) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, err) } p.Payload = res - s.WriteJson(w, p) + return c.JSON(http.StatusOK, p) } else { - res, err := s.dto.ListArticles(r.Context(), 25, 0) + res, err := s.dto.ListArticles(c.Request().Context(), 25, 0) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, err) } p.Payload = res - s.WriteJson(w, p) + return c.JSON(http.StatusOK, p) } } @@ -88,7 +84,7 @@ func (s *Server) listArticles(w http.ResponseWriter, r *http.Request) { // @Tags Articles // @Router /articles/{ID} [get] // @Success 200 {object} ArticleGetResults "OK" -func (s *Server) getArticle(w http.ResponseWriter, r *http.Request) { +func (s *Handler) getArticle(c echo.Context) error { p := ArticleGetResults{ ApiStatusModel: ApiStatusModel{ Message: "OK", @@ -96,22 +92,20 @@ func (s *Server) getArticle(w http.ResponseWriter, r *http.Request) { }, } - id := chi.URLParam(r, "ID") + id := c.Param("ID") uuid, err := uuid.Parse(id) if err != nil { - s.WriteError(w, err.Error(), http.StatusBadRequest) - return + return c.JSON(http.StatusBadRequest, err) } - res, err := s.dto.GetArticle(r.Context(), uuid) + res, err := s.dto.GetArticle(c.Request().Context(), uuid) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, err) } p.Payload = res - s.WriteJson(w, p) + return c.JSON(http.StatusOK, p) } // GetArticleDetails @@ -121,7 +115,7 @@ func (s *Server) getArticle(w http.ResponseWriter, r *http.Request) { // @Tags Articles // @Router /articles/{ID}/details [get] // @Success 200 {object} ArticleDetailsResult "OK" -func (s *Server) getArticleDetails(w http.ResponseWriter, r *http.Request) { +func (s *Handler) getArticleDetails(c echo.Context) error { p := ArticleDetailsResult{ ApiStatusModel: ApiStatusModel{ Message: "OK", @@ -129,22 +123,20 @@ func (s *Server) getArticleDetails(w http.ResponseWriter, r *http.Request) { }, } - id := chi.URLParam(r, "ID") + id := c.Param("ID") uuid, err := uuid.Parse(id) if err != nil { - s.WriteError(w, err.Error(), http.StatusBadRequest) - return + return c.JSON(http.StatusBadRequest, err) } - res, err := s.dto.GetArticleDetails(r.Context(), uuid) + res, err := s.dto.GetArticleDetails(c.Request().Context(), uuid) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, err) } p.Payload = res - s.WriteJson(w, p) + return c.JSON(http.StatusOK, p) } // TODO add page support @@ -156,7 +148,7 @@ func (s *Server) getArticleDetails(w http.ResponseWriter, r *http.Request) { // @Tags Articles // @Router /articles/by/sourceid [get] // @Success 200 {object} ArticlesListResults "OK" -func (s *Server) ListArticlesBySourceId(w http.ResponseWriter, r *http.Request) { +func (s *Handler) ListArticlesBySourceId(c echo.Context) error { p := ArticlesListResults{ ApiStatusModel: ApiStatusModel{ Message: "OK", @@ -164,41 +156,34 @@ func (s *Server) ListArticlesBySourceId(w http.ResponseWriter, r *http.Request) }, } - r.URL.Query() - query := r.URL.Query() - _id := query["id"][0] + id := c.QueryParam("id") - uuid, err := uuid.Parse(_id) + uuid, err := uuid.Parse(id) if err != nil { - s.WriteError(w, err.Error(), http.StatusBadRequest) - return + return c.JSON(http.StatusBadRequest, err) } // if a page number was sent, process it - if len(query["page"]) >= 1 { - _page, err := strconv.Atoi(query["page"][0]) + if len(c.QueryParam("page")) >= 1 { + _page, err := strconv.Atoi(c.QueryParam("page")) if err != nil { - s.WriteError(w, err.Error(), http.StatusBadRequest) - return + return c.JSON(http.StatusBadRequest, err) } - res, err := s.dto.ListNewArticlesBySourceId(r.Context(), uuid, 25, _page) + res, err := s.dto.ListNewArticlesBySourceId(c.Request().Context(), uuid, 25, _page) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, err) } p.Payload = res - s.WriteJson(w, p) } else { - res, err := s.dto.ListNewArticlesBySourceId(r.Context(), uuid, 25, 0) + res, err := s.dto.ListNewArticlesBySourceId(c.Request().Context(), uuid, 25, 0) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, err) } p.Payload = res - s.WriteJson(w, p) } + return c.JSON(http.StatusOK, p) } diff --git a/internal/handler/v1/discordwebhooks.go b/internal/handler/v1/discordwebhooks.go index c0bf57c..38cc14d 100644 --- a/internal/handler/v1/discordwebhooks.go +++ b/internal/handler/v1/discordwebhooks.go @@ -1,15 +1,15 @@ package v1 import ( - "encoding/json" - "log" + "fmt" "net/http" "strings" "git.jamestombleson.com/jtom38/newsbot-api/internal/database" + "git.jamestombleson.com/jtom38/newsbot-api/internal/domain" "git.jamestombleson.com/jtom38/newsbot-api/internal/domain/models" - "github.com/go-chi/chi/v5" "github.com/google/uuid" + "github.com/labstack/echo/v4" ) type ListDiscordWebhooks struct { @@ -22,28 +22,12 @@ type GetDiscordWebhook struct { Payload models.DiscordWebHooksDto `json:"payload"` } -func (s Server) DiscordWebHookRouter() http.Handler { - r := chi.NewRouter() - - r.Get("/", s.ListDiscordWebHooks) - r.Post("/new", s.NewDiscordWebHook) - r.Get("/by/serverAndChannel", s.GetDiscordWebHooksByServerAndChannel) - r.Route("/{ID}", func(r chi.Router) { - r.Get("/", s.GetDiscordWebHooksById) - r.Delete("/", s.deleteDiscordWebHook) - r.Post("/disable", s.disableDiscordWebHook) - r.Post("/enable", s.enableDiscordWebHook) - }) - - return r -} - // ListDiscordWebhooks // @Summary Returns the top 100 entries from the queue to be processed. // @Produce application/json // @Tags Discord, Webhook // @Router /discord/webhooks [get] -func (s *Server) ListDiscordWebHooks(w http.ResponseWriter, r *http.Request) { +func (s *Handler) ListDiscordWebHooks(c echo.Context) error { p := ListDiscordWebhooks{ ApiStatusModel: ApiStatusModel{ Message: "OK", @@ -51,13 +35,12 @@ func (s *Server) ListDiscordWebHooks(w http.ResponseWriter, r *http.Request) { }, } - res, err := s.dto.ListDiscordWebHooks(r.Context(), 50) + res, err := s.dto.ListDiscordWebHooks(c.Request().Context(), 50) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, err) } p.Payload = res - s.WriteJson(w, p) + return c.JSON(http.StatusOK, p) } // GetDiscordWebHook @@ -67,7 +50,7 @@ func (s *Server) ListDiscordWebHooks(w http.ResponseWriter, r *http.Request) { // @Tags Discord, Webhook // @Router /discord/webhooks/{id} [get] // @Success 200 {object} GetDiscordWebhook "OK" -func (s *Server) GetDiscordWebHooksById(w http.ResponseWriter, r *http.Request) { +func (s *Handler) GetDiscordWebHooksById(c echo.Context) error { p := GetDiscordWebhook{ ApiStatusModel: ApiStatusModel{ Message: "OK", @@ -75,25 +58,28 @@ func (s *Server) GetDiscordWebHooksById(w http.ResponseWriter, r *http.Request) }, } - _id := chi.URLParam(r, "ID") + _id := c.Param("ID") if _id == "" { - s.WriteError(w, "id is missing", http.StatusBadRequest) - return + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: ErrIdValueMissing, + }) } uuid, err := uuid.Parse(_id) if err != nil { - s.WriteError(w, "unable to parse id value", http.StatusBadRequest) - return + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: ErrUnableToParseId, + }) } - res, err := s.dto.GetDiscordWebhook(r.Context(), uuid) + res, err := s.dto.GetDiscordWebhook(c.Request().Context(), uuid) if err != nil { - s.WriteError(w, "no record found", http.StatusBadRequest) - return + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: ErrNoRecordFound, + }) } p.Payload = res - s.WriteJson(w, p) + return c.JSON(http.StatusOK, p) } // GetDiscordWebHookByServerAndChannel @@ -104,7 +90,7 @@ func (s *Server) GetDiscordWebHooksById(w http.ResponseWriter, r *http.Request) // @Tags Discord, Webhook // @Router /discord/webhooks/by/serverAndChannel [get] // @Success 200 {object} ListDiscordWebhooks "OK" -func (s *Server) GetDiscordWebHooksByServerAndChannel(w http.ResponseWriter, r *http.Request) { +func (s *Handler) GetDiscordWebHooksByServerAndChannel(c echo.Context) error { p := ListDiscordWebhooks{ ApiStatusModel: ApiStatusModel{ Message: "OK", @@ -112,27 +98,29 @@ func (s *Server) GetDiscordWebHooksByServerAndChannel(w http.ResponseWriter, r * }, } - query := r.URL.Query() - _server := query["server"][0] + _server := c.QueryParam("server") if _server == "" { - s.WriteError(w, "ID is missing", http.StatusInternalServerError) - return + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: ErrIdValueMissing, + }) } - _channel := query["channel"][0] + _channel := c.QueryParam("channel") if _channel == "" { - s.WriteError(w, "Channel is missing", http.StatusInternalServerError) - return + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: fmt.Sprintf("%s channel", ErrParameterMissing), + }) } - res, err := s.dto.GetDiscordWebHookByServerAndChannel(r.Context(), _server, _channel) + res, err := s.dto.GetDiscordWebHookByServerAndChannel(c.Request().Context(), _server, _channel) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ + Message: err.Error(), + }) } p.Payload = res - s.WriteJson(w, p) + return c.JSON(http.StatusOK, p) } // NewDiscordWebHook @@ -142,25 +130,30 @@ func (s *Server) GetDiscordWebHooksByServerAndChannel(w http.ResponseWriter, r * // @Param channel query string true "Channel name" // @Tags Discord, Webhook // @Router /discord/webhooks/new [post] -func (s *Server) NewDiscordWebHook(w http.ResponseWriter, r *http.Request) { - query := r.URL.Query() - _url := query["url"][0] - _server := query["server"][0] - _channel := query["channel"][0] +func (s *Handler) NewDiscordWebHook(c echo.Context) error { + _url := c.QueryParam("url") + _server := c.QueryParam("server") + _channel := c.QueryParam("channel") if _url == "" { - http.Error(w, "url is missing a value", http.StatusBadRequest) - return + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: "url is missing a value", + }) } if !strings.Contains(_url, "discord.com/api/webhooks") { - http.Error(w, "invalid url", http.StatusBadRequest) - return + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: "invalid url", + }) } if _server == "" { - http.Error(w, "server is missing", http.StatusBadRequest) + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: "server is missing", + }) } if _channel == "" { - http.Error(w, "channel is missing", http.StatusBadRequest) + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: "channel is missing", + }) } params := database.CreateDiscordWebHookParams{ ID: uuid.New(), @@ -169,14 +162,9 @@ func (s *Server) NewDiscordWebHook(w http.ResponseWriter, r *http.Request) { Channel: _channel, Enabled: true, } - s.Db.CreateDiscordWebHook(r.Context(), params) + s.Db.CreateDiscordWebHook(c.Request().Context(), params) - bJson, err := json.Marshal(¶ms) - if err != nil { - log.Panicln(err) - } - w.Header().Set("Content-Type", "application/json") - w.Write(bJson) + return c.JSON(http.StatusOK, params) } // DisableDiscordWebHooks @@ -184,25 +172,28 @@ func (s *Server) NewDiscordWebHook(w http.ResponseWriter, r *http.Request) { // @Param id path string true "id" // @Tags Discord, Webhook // @Router /discord/webhooks/{ID}/disable [post] -func (s *Server) disableDiscordWebHook(w http.ResponseWriter, r *http.Request) { - id := chi.URLParam(r, "ID") +func (s *Handler) disableDiscordWebHook(c echo.Context) error { + id := c.Param("ID") uuid, err := uuid.Parse(id) if err != nil { - s.WriteError(w, err.Error(), http.StatusBadRequest) - return + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: err.Error(), + }) } // Check to make sure we can find the record - _, err = s.Db.GetDiscordWebHooksByID(r.Context(), uuid) + _, err = s.Db.GetDiscordWebHooksByID(c.Request().Context(), uuid) if err != nil { - s.WriteError(w, err.Error(), http.StatusBadRequest) - return + return c.JSON(http.StatusInternalServerError, err.Error()) } - err = s.Db.DisableDiscordWebHook(r.Context(), uuid) + err = s.Db.DisableDiscordWebHook(c.Request().Context(), uuid) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) + return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ + Message: err.Error(), + }) } + return nil } // EnableDiscordWebHook @@ -210,23 +201,26 @@ func (s *Server) disableDiscordWebHook(w http.ResponseWriter, r *http.Request) { // @Param id path string true "id" // @Tags Discord, Webhook // @Router /discord/webhooks/{ID}/enable [post] -func (s *Server) enableDiscordWebHook(w http.ResponseWriter, r *http.Request) { - id := chi.URLParam(r, "ID") +func (s *Handler) enableDiscordWebHook(c echo.Context) error { + id := c.Param("ID") uuid, err := uuid.Parse(id) if err != nil { - s.WriteError(w, err.Error(), http.StatusBadRequest) + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: err.Error(), + }) } // Check to make sure we can find the record - _, err = s.Db.GetDiscordWebHooksByID(r.Context(), uuid) + _, err = s.Db.GetDiscordWebHooksByID(c.Request().Context(), uuid) if err != nil { - s.WriteError(w, err.Error(), http.StatusBadRequest) + return c.JSON(http.StatusInternalServerError, err.Error()) } - err = s.Db.EnableDiscordWebHook(r.Context(), uuid) + err = s.Db.EnableDiscordWebHook(c.Request().Context(), uuid) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) + return c.JSON(http.StatusInternalServerError, err.Error()) } + return nil } // DeleteDiscordWebHook @@ -234,26 +228,28 @@ func (s *Server) enableDiscordWebHook(w http.ResponseWriter, r *http.Request) { // @Param id path string true "id" // @Tags Discord, Webhook // @Router /discord/webhooks/{ID} [delete] -func (s *Server) deleteDiscordWebHook(w http.ResponseWriter, r *http.Request) { +func (s *Handler) deleteDiscordWebHook(c echo.Context) error { //var item model.Sources = model.Sources{} - id := chi.URLParam(r, "ID") + id := c.Param("ID") uuid, err := uuid.Parse(id) if err != nil { - s.WriteError(w, err.Error(), http.StatusBadRequest) + return c.JSON(http.StatusBadRequest, err.Error()) } // Check to make sure we can find the record - _, err = s.Db.GetDiscordQueueByID(r.Context(), uuid) + _, err = s.Db.GetDiscordQueueByID(c.Request().Context(), uuid) if err != nil { - s.WriteError(w, err.Error(), http.StatusBadRequest) + return c.JSON(http.StatusInternalServerError, err.Error()) } // Delete the record - err = s.Db.DeleteDiscordWebHooks(r.Context(), uuid) + err = s.Db.DeleteDiscordWebHooks(c.Request().Context(), uuid) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) + return c.JSON(http.StatusInternalServerError, err.Error()) } + + return nil } // UpdateDiscordWebHook @@ -261,17 +257,19 @@ func (s *Server) deleteDiscordWebHook(w http.ResponseWriter, r *http.Request) { // @Param id path string true "id" // @Tags Discord, Webhook // @Router /discord/webhooks/{id} [patch] -func (s *Server) UpdateDiscordWebHook(w http.ResponseWriter, r *http.Request) { - id := chi.URLParam(r, "ID") +func (s *Handler) UpdateDiscordWebHook(c echo.Context) error { + id := c.Param("ID") uuid, err := uuid.Parse(id) if err != nil { - log.Panicln(err) + return c.JSON(http.StatusInternalServerError, err.Error()) } // Check to make sure we can find the record - _, err = s.Db.GetDiscordQueueByID(r.Context(), uuid) + _, err = s.Db.GetDiscordQueueByID(c.Request().Context(), uuid) if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) + return c.JSON(http.StatusInternalServerError, err.Error()) } + + return nil } diff --git a/internal/handler/v1/handler.go b/internal/handler/v1/handler.go new file mode 100644 index 0000000..c1c3e93 --- /dev/null +++ b/internal/handler/v1/handler.go @@ -0,0 +1,151 @@ +package v1 + +import ( + "context" + "database/sql" + + "github.com/labstack/echo/v4" + _ "github.com/lib/pq" + swagger "github.com/swaggo/echo-swagger" + + "git.jamestombleson.com/jtom38/newsbot-api/internal/database" + "git.jamestombleson.com/jtom38/newsbot-api/internal/domain" + "git.jamestombleson.com/jtom38/newsbot-api/internal/services" + "git.jamestombleson.com/jtom38/newsbot-api/internal/services/dto" +) + +type Handler struct { + Router *echo.Echo + Db *database.Queries + dto *dto.DtoClient + //ctx *context.Context +} + +const ( + HeaderContentType = "Content-Type" + + ApplicationJson = "application/json" + + ErrParameterIdMissing = "The requested parameter ID was not found." + ErrParameterMissing = "The requested parameter was found found:" + ErrUnableToParseId = "Unable to parse the requested ID." + ErrRecordMissing = "The requested record was not found" +) + +var ( + ErrIdValueMissing string = "id value is missing" + ErrValueNotUuid string = "a value given was expected to be a uuid but was not correct." + ErrNoRecordFound string = "no record was found." + ErrUnableToConvertToJson string = "Unable to convert to json" +) + +func NewServer(ctx context.Context, db *database.Queries) *Handler { + s := &Handler{ + //ctx: &ctx, + Db: db, + dto: dto.NewDtoClient(db), + } + + db, err := openDatabase(ctx) + if err != nil { + panic(err) + } + s.Db = db + + router := echo.New() + router.GET("/swagger/*", swagger.WrapHandler) + + v1 := router.Group("/api/v1") + articles := v1.Group("/articles") + articles.GET("/", s.listArticles) + articles.GET("/:id", s.getArticle) + articles.GET("/:id/details", s.getArticleDetails) + articles.GET("/by/source/:id", s.ListArticlesBySourceId) + + dwh := v1.Group("/discord/webhooks") + dwh.GET("/", s.ListDiscordWebHooks) + dwh.POST("/new", s.NewDiscordWebHook) + dwh.GET("/by/serverAndChannel", s.GetDiscordWebHooksByServerAndChannel) + dwh.GET("/:ID", s.GetDiscordWebHooksById) + dwh.DELETE("/:ID", s.deleteDiscordWebHook) + dwh.POST("/:ID/disable", s.disableDiscordWebHook) + dwh.POST("/:ID/enable", s.enableDiscordWebHook) + + queue := v1.Group("/queue") + queue.GET("/discord/webhooks", s.ListDiscordWebhookQueue) // TODO this needs to be reworked + + settings := v1.Group("/settings") + settings.GET("/", s.getSettings) + + sources := v1.Group("/sources") + sources.GET("/", s.listSources) + sources.GET("/by/source", s.listSourcesBySource) + sources.GET("/by/sourceAndName", s.GetSourceBySourceAndName) + sources.POST("/new/reddit", s.newRedditSource) + sources.POST("/new/youtube", s.newYoutubeSource) + sources.POST("/new/twitch", s.newTwitchSource) + + sources.GET("/:ID/", s.getSources) + sources.DELETE("/:ID/", s.deleteSources) + sources.POST("/:ID/disable", s.disableSource) + sources.POST("/:ID/enable", s.enableSource) + + subs := v1.Group("/subscriptions") + subs.GET("/", s.ListSubscriptions) + subs.GET("/details", s.ListSubscriptionDetails) + subs.GET("/by/discordId", s.GetSubscriptionsByDiscordId) + subs.GET("/by/sourceId", s.GetSubscriptionsBySourceId) + subs.POST("/discord/webhook/new", s.newDiscordWebHookSubscription) + subs.DELETE("/discord/webhook/delete", s.DeleteDiscordWebHookSubscription) + + return s +} + +func openDatabase(ctx context.Context) (*database.Queries, error) { + _env := services.NewConfig() + connString := _env.GetConfig(services.Sql_Connection_String) + db, err := sql.Open("postgres", connString) + if err != nil { + panic(err) + } + + queries := database.New(db) + return queries, err +} + +func (s *Handler) MountRoutes() { + //s.Router.Get("/swagger/*", httpSwagger.Handler( + // httpSwagger.URL("doc.json"), //The url pointing to API definition + //)) + //s.Router.Get("/api/settings", s.getSettings) + + //s.Router.Mount("/api/sources", s.GetSourcesRouter()) + //s.Router.Mount("/api/subscriptions", s.GetSubscriptionsRouter()) +} + +type ApiStatusModel struct { + StatusCode int `json:"status"` + Message string `json:"message"` +} + +type ApiError struct { + *ApiStatusModel +} + +func (s *Handler) WriteError(c echo.Context, errMessage string, HttpStatusCode int) error { + return c.JSON(HttpStatusCode, domain.ErrorResponse{ + Message: errMessage, + }) +} + +//func (s *Handler) WriteJson(w http.ResponseWriter, model interface{}) { +// w.Header().Set(HeaderContentType, ApplicationJson) +// +// bres, err := json.Marshal(model) +// if err != nil { +// s.WriteError(w, err.Error(), http.StatusInternalServerError) +// return +// } +// +// w.Write(bres) +//} diff --git a/internal/handler/v1/queue.go b/internal/handler/v1/queue.go index 1ea3521..1f8ad59 100644 --- a/internal/handler/v1/queue.go +++ b/internal/handler/v1/queue.go @@ -3,8 +3,9 @@ package v1 import ( "net/http" + "git.jamestombleson.com/jtom38/newsbot-api/internal/domain" "git.jamestombleson.com/jtom38/newsbot-api/internal/domain/models" - "github.com/go-chi/chi/v5" + "github.com/labstack/echo/v4" ) type ListDiscordWebHooksQueueResults struct { @@ -12,21 +13,13 @@ type ListDiscordWebHooksQueueResults struct { Payload []models.DiscordQueueDetailsDto `json:"payload"` } -func (s *Server) GetQueueRouter() http.Handler { - r := chi.NewRouter() - - r.Get("/discord/webhooks", s.ListDiscordWebhookQueue) - - return r -} - // GetDiscordQueue // @Summary Returns the top 100 entries from the queue to be processed. // @Produce application/json // @Tags Queue // @Router /queue/discord/webhooks [get] // @Success 200 {object} ListDiscordWebHooksQueueResults "ok" -func (s *Server) ListDiscordWebhookQueue(w http.ResponseWriter, r *http.Request) { +func (s *Handler) ListDiscordWebhookQueue(c echo.Context) error { p := ListDiscordWebHooksQueueResults{ ApiStatusModel: ApiStatusModel{ Message: "OK", @@ -35,12 +28,13 @@ func (s *Server) ListDiscordWebhookQueue(w http.ResponseWriter, r *http.Request) } // Get the raw resp from sql - res, err := s.dto.ListDiscordWebhookQueueDetails(r.Context(), 50) + res, err := s.dto.ListDiscordWebhookQueueDetails(c.Request().Context(), 50) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ + Message: err.Error(), + }) } p.Payload = res - s.WriteJson(w, p) + return c.JSON(http.StatusOK, p) } diff --git a/internal/handler/v1/server.go b/internal/handler/v1/server.go deleted file mode 100644 index 4c4c288..0000000 --- a/internal/handler/v1/server.go +++ /dev/null @@ -1,127 +0,0 @@ -package v1 - -import ( - "context" - "database/sql" - "encoding/json" - "net/http" - - "github.com/go-chi/chi/v5" - "github.com/go-chi/chi/v5/middleware" - _ "github.com/lib/pq" - httpSwagger "github.com/swaggo/http-swagger" - - "git.jamestombleson.com/jtom38/newsbot-api/internal/database" - "git.jamestombleson.com/jtom38/newsbot-api/internal/services" - "git.jamestombleson.com/jtom38/newsbot-api/internal/services/dto" -) - -type Server struct { - Router *chi.Mux - Db *database.Queries - dto *dto.DtoClient - //ctx *context.Context -} - -const ( - HeaderContentType = "Content-Type" - - ApplicationJson = "application/json" -) - -var ( - ErrIdValueMissing string = "id value is missing" - ErrValueNotUuid string = "a value given was expected to be a uuid but was not correct." - ErrNoRecordFound string = "no record was found." - ErrUnableToConvertToJson string = "Unable to convert to json" -) - -func NewServer(ctx context.Context, db *database.Queries) *Server { - s := &Server{ - //ctx: &ctx, - Db: db, - dto: dto.NewDtoClient(db), - } - - db, err := openDatabase(ctx) - if err != nil { - panic(err) - } - s.Db = db - - s.Router = chi.NewRouter() - s.MountMiddleware() - s.MountRoutes() - return s -} - -func openDatabase(ctx context.Context) (*database.Queries, error) { - _env := services.NewConfig() - connString := _env.GetConfig(services.Sql_Connection_String) - db, err := sql.Open("postgres", connString) - if err != nil { - panic(err) - } - - queries := database.New(db) - return queries, err -} - -func (s *Server) MountMiddleware() { - s.Router.Use(middleware.Logger) - s.Router.Use(middleware.Recoverer) - //s.Router.Use(middleware.Heartbeat()) -} - -func (s *Server) MountRoutes() { - s.Router.Get("/swagger/*", httpSwagger.Handler( - httpSwagger.URL("doc.json"), //The url pointing to API definition - )) - - s.Router.Mount("/api/articles", s.GetArticleRouter()) - s.Router.Mount("/api/queue", s.GetQueueRouter()) - s.Router.Mount("/api/discord/webhooks", s.DiscordWebHookRouter()) - - //s.Router.Get("/api/settings", s.getSettings) - - s.Router.Mount("/api/sources", s.GetSourcesRouter()) - s.Router.Mount("/api/subscriptions", s.GetSubscriptionsRouter()) -} - -type ApiStatusModel struct { - StatusCode int `json:"status"` - Message string `json:"message"` -} - -type ApiError struct { - *ApiStatusModel -} - -func (s *Server) WriteError(w http.ResponseWriter, errMessage string, HttpStatusCode int) { - w.Header().Set(HeaderContentType, ApplicationJson) - e := ApiError{ - ApiStatusModel: &ApiStatusModel{ - StatusCode: HttpStatusCode, - Message: errMessage, - }, - } - - b, err := json.Marshal(e) - if err != nil { - http.Error(w, err.Error(), HttpStatusCode) - } - - w.Write(b) -} - -func (s *Server) WriteJson(w http.ResponseWriter, model interface{}) { - w.Header().Set(HeaderContentType, ApplicationJson) - - bres, err := json.Marshal(model) - if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Write(bres) -} diff --git a/internal/handler/v1/settings.go b/internal/handler/v1/settings.go index 8628fbb..96b472d 100644 --- a/internal/handler/v1/settings.go +++ b/internal/handler/v1/settings.go @@ -4,41 +4,36 @@ import ( "encoding/json" "net/http" - "github.com/go-chi/chi/v5" + "git.jamestombleson.com/jtom38/newsbot-api/internal/domain" "github.com/google/uuid" + "github.com/labstack/echo/v4" ) -func (s *Server) getSettings(w http.ResponseWriter, r *http.Request) { - // GetSettings - // @Summary Returns a object based on the Key that was given. - // @Param key path string true "Settings Key value" - // @Produce application/json - // @Tags Settings - // @Router /settings/{key} [get] - - - w.Header().Set("Content-Type", "application/json") - - //var item model.Sources - id := chi.URLParam(r, "ID") +// GetSettings +// @Summary Returns a object based on the Key that was given. +// @Param key path string true "Settings Key value" +// @Produce application/json +// @Tags Settings +// @Router /settings/{key} [get] +func (s *Handler) getSettings(c echo.Context) error { + id := c.Param("ID") uuid, err := uuid.Parse(id) if err != nil { - s.WriteError(w, err.Error(), http.StatusBadRequest) - return + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: err.Error(), + }) } - res, err := s.Db.GetSourceByID(r.Context(), uuid) + res, err := s.Db.GetSourceByID(c.Request().Context(), uuid) if err != nil { - s.WriteError(w, err.Error(), http.StatusNotFound) - return + return c.JSON(http.StatusInternalServerError, err.Error()) } bResult, err := json.Marshal(res) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, err.Error()) } - w.Write(bResult) + return c.JSON(http.StatusOK, bResult) } diff --git a/internal/handler/v1/sources.go b/internal/handler/v1/sources.go index c9751ec..cadd521 100644 --- a/internal/handler/v1/sources.go +++ b/internal/handler/v1/sources.go @@ -1,34 +1,24 @@ package v1 import ( + "context" "encoding/json" "fmt" "net/http" "strings" "git.jamestombleson.com/jtom38/newsbot-api/internal/database" + "git.jamestombleson.com/jtom38/newsbot-api/internal/domain" "git.jamestombleson.com/jtom38/newsbot-api/internal/domain/models" "github.com/go-chi/chi/v5" "github.com/google/uuid" + "github.com/labstack/echo/v4" ) -func (s *Server) GetSourcesRouter() http.Handler { +func (s *Handler) GetSourcesRouter() http.Handler { r := chi.NewRouter() - r.Get("/", s.listSources) - r.Get("/by/source", s.listSourcesBySource) - r.Get("/by/sourceAndName", s.GetSourceBySourceAndName) - - r.Post("/new/reddit", s.newRedditSource) - r.Post("/new/youtube", s.newYoutubeSource) - r.Post("/new/twitch", s.newTwitchSource) - - r.Route("/{ID}", func(p chi.Router) { - p.Get("/", s.getSources) - p.Delete("/", s.deleteSources) - p.Post("/disable", s.disableSource) - p.Post("/enable", s.enableSource) - }) + return r } @@ -50,7 +40,7 @@ type GetSource struct { // @Router /sources [get] // @Success 200 {object} ListSources "ok" // @Failure 400 {object} ApiError "Unable to reach SQL or Data problems" -func (s *Server) listSources(w http.ResponseWriter, r *http.Request) { +func (s *Handler) listSources(c echo.Context) error { //TODO Add top? /* top := chi.URLParam(r, "top") @@ -69,14 +59,15 @@ func (s *Server) listSources(w http.ResponseWriter, r *http.Request) { } // Default way of showing all sources - items, err := s.dto.ListSources(r.Context(), 50) + items, err := s.dto.ListSources(c.Request().Context(), 50) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ + Message: err.Error(), + }) } p.Payload = items - s.WriteJson(w, p) + return c.JSON(http.StatusOK, p) } // ListSourcesBySource @@ -88,7 +79,7 @@ func (s *Server) listSources(w http.ResponseWriter, r *http.Request) { // @Success 200 {object} ListSources "ok" // @Failure 400 {object} ApiError "Unable to query SQL." // @Failure 500 {object} ApiError "Problems with data." -func (s *Server) listSourcesBySource(w http.ResponseWriter, r *http.Request) { +func (s *Handler) listSourcesBySource(c echo.Context) error { //TODO Add top? /* top := chi.URLParam(r, "top") @@ -106,17 +97,18 @@ func (s *Server) listSourcesBySource(w http.ResponseWriter, r *http.Request) { }, } - query := r.URL.Query() + source := c.QueryParam("source") // Shows the list by Sources.source - res, err := s.dto.ListSourcesBySource(r.Context(), query["source"][0]) + res, err := s.dto.ListSourcesBySource(c.Request().Context(), source) if err != nil { - s.WriteError(w, err.Error(), http.StatusBadRequest) - return + return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ + Message: err.Error(), + }) } p.Payload = res - s.WriteJson(w, p) + return c.JSON(http.StatusOK, p) } // GetSource @@ -129,7 +121,7 @@ func (s *Server) listSourcesBySource(w http.ResponseWriter, r *http.Request) { // @Failure 204 {object} ApiError "No record found." // @Failure 400 {object} ApiError "Unable to query SQL." // @Failure 500 {object} ApiError "Failed to process data from SQL." -func (s *Server) getSources(w http.ResponseWriter, r *http.Request) { +func (s *Handler) getSources(c echo.Context) error { payload := GetSource{ ApiStatusModel: ApiStatusModel{ Message: "OK", @@ -137,20 +129,23 @@ func (s *Server) getSources(w http.ResponseWriter, r *http.Request) { }, } - uuid, err := uuid.Parse(chi.URLParam(r, "ID")) + id := c.Param("ID") + uuid, err := uuid.Parse(id) if err != nil { - s.WriteError(w, err.Error(), http.StatusBadRequest) - return + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: ErrUnableToParseId, + }) } - res, err := s.dto.GetSourceById(r.Context(), uuid) + res, err := s.dto.GetSourceById(c.Request().Context(), uuid) if err != nil { - s.WriteError(w, err.Error(), http.StatusNoContent) - return + return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ + Message: ErrNoRecordFound, + }) } payload.Payload = res - s.WriteJson(w, payload) + return c.JSON(http.StatusOK, payload) } // GetSourceByNameAndSource @@ -164,7 +159,7 @@ func (s *Server) getSources(w http.ResponseWriter, r *http.Request) { // @Failure 204 {object} ApiError "No record found." // @Failure 400 {object} ApiError "Unable to query SQL." // @Failure 500 {object} ApiError "Failed to process data from SQL." -func (s *Server) GetSourceBySourceAndName(w http.ResponseWriter, r *http.Request) { +func (s *Handler) GetSourceBySourceAndName(c echo.Context) error { p := GetSource{ ApiStatusModel: ApiStatusModel{ Message: "OK", @@ -172,27 +167,33 @@ func (s *Server) GetSourceBySourceAndName(w http.ResponseWriter, r *http.Request }, } - query := r.URL.Query() - name := query["name"][0] - if name == "" { - s.WriteError(w, "Parameter 'name' was missing in the query.", http.StatusInternalServerError) - return - } - - source := query["source"][0] - if source == "" { - s.WriteError(w, "The parameter 'source' was missing in the query.", http.StatusInternalServerError) - return - } - - item, err := s.dto.GetSourceByNameAndSource(r.Context(), name, source) + var param domain.GetSourceBySourceAndNameParamRequest + err := c.Bind(¶m) if err != nil { - s.WriteError(w, "Unable to find the requested record.", http.StatusInternalServerError) - return + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: err.Error(), + }) + } + + //name := c.QueryParam("name") + //if name == "" { + // s.WriteError(w, "Parameter 'name' was missing in the query.", http.StatusInternalServerError) + // return c.JSON(http.bad) + //} + + //source := query["source"][0] + //if source == "" { + // s.WriteError(w, "The parameter 'source' was missing in the query.", http.StatusInternalServerError) + // return + //} + + item, err := s.dto.GetSourceByNameAndSource(c.Request().Context(), param.Name, param.Source) + if err != nil { + return c.JSON(http.StatusInternalServerError, err.Error()) } p.Payload = item - s.WriteJson(w, p) + return c.JSON(http.StatusOK, p) } // NewRedditSource @@ -201,19 +202,28 @@ func (s *Server) GetSourceBySourceAndName(w http.ResponseWriter, r *http.Request // @Param url query string true "url" // @Tags Source // @Router /sources/new/reddit [post] -func (s *Server) newRedditSource(w http.ResponseWriter, r *http.Request) { - query := r.URL.Query() - _name := query["name"][0] - _url := query["url"][0] +func (s *Handler) newRedditSource(c echo.Context) error { + var param domain.NewSourceParamRequest + err := c.Bind(¶m) + if err != nil { + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: err.Error(), + }) + } + //query := r.URL.Query() + //_name := query["name"][0] + //_url := query["url"][0] //_tags := query["tags"][0] - if _url == "" { - s.WriteError(w, "url is missing a value", http.StatusBadRequest) - return + if param.Url == "" { + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: "Url is missing a value", + }) } - if !strings.Contains(_url, "reddit.com") { - s.WriteError(w, "invalid url", http.StatusBadRequest) - return + if !strings.Contains(param.Url, "reddit.com") { + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: "Invalid URL given", + }) } /* @@ -223,32 +233,33 @@ func (s *Server) newRedditSource(w http.ResponseWriter, r *http.Request) { } else { } */ - tags := fmt.Sprintf("twitch, %v", _name) + + tags := fmt.Sprintf("twitch, %v", param.Name) params := database.CreateSourceParams{ ID: uuid.New(), Site: "reddit", - Name: _name, + Name: param.Name, Source: "reddit", Type: "feed", Enabled: true, - Url: _url, + Url: param.Url, Tags: tags, } - err := s.Db.CreateSource(r.Context(), params) + err = s.Db.CreateSource(c.Request().Context(), params) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ + Message: err.Error(), + }) } //s.WriteJson(w, ¶ms) bJson, err := json.Marshal(¶ms) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, err.Error()) } - w.Write(bJson) + return c.JSON(http.StatusOK, bJson) } // NewYoutubeSource @@ -257,20 +268,29 @@ func (s *Server) newRedditSource(w http.ResponseWriter, r *http.Request) { // @Param url query string true "url" // @Tags Source // @Router /sources/new/youtube [post] -func (s *Server) newYoutubeSource(w http.ResponseWriter, r *http.Request) { - query := r.URL.Query() - _name := query["name"][0] - _url := query["url"][0] - //_tags := query["tags"][0] - - w.Header().Set("Content-Type", "application/json") - if _url == "" { - s.WriteError(w, "url is missing a value", http.StatusBadRequest) - return +func (s *Handler) newYoutubeSource(c echo.Context) error { + var param domain.NewSourceParamRequest + err := c.Bind(¶m) + if err != nil { + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: err.Error(), + }) } - if !strings.Contains(_url, "youtube.com") { - s.WriteError(w, "invalid url", http.StatusBadRequest) - return + + //query := r.URL.Query() + //_name := query["name"][0] + //_url := query["url"][0] + ////_tags := query["tags"][0] + + if param.Url == "" { + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: "url is missing a value", + }) + } + if !strings.Contains(param.Url, "youtube.com") { + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: "Invalid URL", + }) } /* @@ -279,31 +299,31 @@ func (s *Server) newYoutubeSource(w http.ResponseWriter, r *http.Request) { } else { } */ - tags := fmt.Sprintf("twitch, %v", _name) + tags := fmt.Sprintf("twitch, %v", param.Name) params := database.CreateSourceParams{ ID: uuid.New(), Site: "youtube", - Name: _name, + Name: param.Name, Source: "youtube", Type: "feed", Enabled: true, - Url: _url, + Url: param.Url, Tags: tags, } - err := s.Db.CreateSource(r.Context(), params) + err = s.Db.CreateSource(context.Background(), params) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, err.Error()) } bJson, err := json.Marshal(¶ms) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ + Message: err.Error(), + }) } - w.Write(bJson) + return c.JSON(http.StatusOK, bJson) } // NewTwitchSource @@ -311,38 +331,46 @@ func (s *Server) newYoutubeSource(w http.ResponseWriter, r *http.Request) { // @Param name query string true "name" // @Tags Source // @Router /sources/new/twitch [post] -func (s *Server) newTwitchSource(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") +func (s *Handler) newTwitchSource(c echo.Context) error { + var param domain.NewSourceParamRequest + err := c.Bind(¶m) + if err != nil { + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: err.Error(), + }) + } - query := r.URL.Query() - _name := query["name"][0] + //query := r.URL.Query() + //_name := query["name"][0] - tags := fmt.Sprintf("twitch, %v", _name) - _url := fmt.Sprintf("https://twitch.tv/%v", _name) + tags := fmt.Sprintf("twitch, %v", param.Name) + _url := fmt.Sprintf("https://twitch.tv/%v", param.Name) params := database.CreateSourceParams{ ID: uuid.New(), Site: "twitch", - Name: _name, + Name: param.Name, Source: "twitch", Type: "api", Enabled: true, Url: _url, Tags: tags, } - err := s.Db.CreateSource(r.Context(), params) + err = s.Db.CreateSource(c.Request().Context(), params) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ + Message: err.Error(), + }) } bJson, err := json.Marshal(¶ms) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ + Message: err.Error(), + }) } - w.Write(bJson) + return c.JSON(http.StatusOK, bJson) } // DeleteSource @@ -350,28 +378,29 @@ func (s *Server) newTwitchSource(w http.ResponseWriter, r *http.Request) { // @Param id path string true "id" // @Tags Source // @Router /sources/{id} [POST] -func (s *Server) deleteSources(w http.ResponseWriter, r *http.Request) { - //var item model.Sources = model.Sources{} - - id := chi.URLParam(r, "ID") +func (s *Handler) deleteSources(c echo.Context) error { + id := c.Param("ID") uuid, err := uuid.Parse(id) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: err.Error(), + }) } // Check to make sure we can find the record - _, err = s.Db.GetSourceByID(r.Context(), uuid) + _, err = s.Db.GetSourceByID(c.Request().Context(), uuid) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ + Message: err.Error(), + }) } // Delete the record - err = s.Db.DeleteSource(r.Context(), uuid) + err = s.Db.DeleteSource(c.Request().Context(), uuid) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ + Message: err.Error(), + }) } p := ApiStatusModel{ @@ -381,11 +410,12 @@ func (s *Server) deleteSources(w http.ResponseWriter, r *http.Request) { b, err := json.Marshal(p) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ + Message: err.Error(), + }) } - w.Write(b) + return c.JSON(http.StatusOK, b) } // DisableSource @@ -393,22 +423,28 @@ func (s *Server) deleteSources(w http.ResponseWriter, r *http.Request) { // @Param id path string true "id" // @Tags Source // @Router /sources/{id}/disable [post] -func (s *Server) disableSource(w http.ResponseWriter, r *http.Request) { - id := chi.URLParam(r, "ID") +func (s *Handler) disableSource(c echo.Context) error { + id := c.Param("ID") uuid, err := uuid.Parse(id) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) + return c.JSON(http.StatusBadRequest, domain.ErrorResponse{ + Message: err.Error(), + }) } // Check to make sure we can find the record - _, err = s.Db.GetSourceByID(r.Context(), uuid) + _, err = s.Db.GetSourceByID(c.Request().Context(), uuid) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) + return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ + Message: err.Error(), + }) } - err = s.Db.DisableSource(r.Context(), uuid) + err = s.Db.DisableSource(context.Background(), uuid) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) + return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ + Message: err.Error(), + }) } p := ApiStatusModel{ @@ -418,11 +454,12 @@ func (s *Server) disableSource(w http.ResponseWriter, r *http.Request) { b, err := json.Marshal(p) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ + Message: err.Error(), + }) } - w.Write(b) + return c.JSON(http.StatusOK, b) } // EnableSource @@ -430,22 +467,26 @@ func (s *Server) disableSource(w http.ResponseWriter, r *http.Request) { // @Param id path string true "id" // @Tags Source // @Router /sources/{id}/enable [post] -func (s *Server) enableSource(w http.ResponseWriter, r *http.Request) { - id := chi.URLParam(r, "ID") +func (s *Handler) enableSource(c echo.Context) error { + id := c.Param("ID") uuid, err := uuid.Parse(id) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) + return c.JSON(http.StatusBadRequest, err.Error()) } // Check to make sure we can find the record - _, err = s.Db.GetSourceByID(r.Context(), uuid) + _, err = s.Db.GetSourceByID(c.Request().Context(), uuid) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) + return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ + Message: err.Error(), + }) } - err = s.Db.EnableSource(r.Context(), uuid) + err = s.Db.EnableSource(c.Request().Context(), uuid) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) + return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ + Message: err.Error(), + }) } p := ApiStatusModel{ @@ -455,9 +496,10 @@ func (s *Server) enableSource(w http.ResponseWriter, r *http.Request) { b, err := json.Marshal(p) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ + Message: err.Error(), + }) } - w.Write(b) + return c.JSON(http.StatusOK, b) } diff --git a/internal/handler/v1/subscriptions.go b/internal/handler/v1/subscriptions.go index f6de352..7af0ccc 100644 --- a/internal/handler/v1/subscriptions.go +++ b/internal/handler/v1/subscriptions.go @@ -7,23 +7,10 @@ import ( "git.jamestombleson.com/jtom38/newsbot-api/internal/database" "git.jamestombleson.com/jtom38/newsbot-api/internal/domain/models" - "github.com/go-chi/chi/v5" "github.com/google/uuid" + "github.com/labstack/echo/v4" ) -func (s *Server) GetSubscriptionsRouter() http.Handler { - r := chi.NewRouter() - - r.Get("/", s.ListSubscriptions) - r.Get("/details", s.ListSubscriptionDetails) - r.Get("/by/discordId", s.GetSubscriptionsByDiscordId) - r.Get("/by/sourceId", s.GetSubscriptionsBySourceId) - r.Post("/discord/webhook/new", s.newDiscordWebHookSubscription) - r.Delete("/discord/webhook/delete", s.DeleteDiscordWebHookSubscription) - - return r -} - type ListSubscriptions struct { ApiStatusModel Payload []models.SubscriptionDto `json:"payload"` @@ -47,7 +34,7 @@ type ListSubscriptionDetails struct { // @Success 200 {object} ListSubscriptions "ok" // @Failure 400 {object} ApiError "Unable to reach SQL." // @Failure 500 {object} ApiError "Failed to process data from SQL." -func (s *Server) ListSubscriptions(w http.ResponseWriter, r *http.Request) { +func (s *Handler) ListSubscriptions(c echo.Context) error { payload := ListSubscriptions{ ApiStatusModel: ApiStatusModel{ StatusCode: http.StatusOK, @@ -55,14 +42,13 @@ func (s *Server) ListSubscriptions(w http.ResponseWriter, r *http.Request) { }, } - res, err := s.dto.ListSubscriptions(r.Context(), 50) + res, err := s.dto.ListSubscriptions(c.Request().Context(), 50) if err != nil { - s.WriteError(w, err.Error(), http.StatusBadRequest) - return + return s.WriteError(c, err.Error(), http.StatusBadRequest) } payload.Payload = res - s.WriteJson(w, payload) + return c.JSON(http.StatusOK, payload) } // ListSubscriptionDetails @@ -71,7 +57,7 @@ func (s *Server) ListSubscriptions(w http.ResponseWriter, r *http.Request) { // @Tags Subscription // @Router /subscriptions/details [get] // @Success 200 {object} ListSubscriptionDetails "ok" -func (s *Server) ListSubscriptionDetails(w http.ResponseWriter, r *http.Request) { +func (s *Handler) ListSubscriptionDetails(c echo.Context) error { payload := ListSubscriptionDetails{ ApiStatusModel: ApiStatusModel{ StatusCode: http.StatusOK, @@ -79,14 +65,13 @@ func (s *Server) ListSubscriptionDetails(w http.ResponseWriter, r *http.Request) }, } - res, err := s.dto.ListSubscriptionDetails(r.Context(), 50) + res, err := s.dto.ListSubscriptionDetails(c.Request().Context(), 50) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return s.WriteError(c, err.Error(), http.StatusInternalServerError) } payload.Payload = res - s.WriteJson(w, payload) + return c.JSON(http.StatusOK, payload) } // GetSubscriptionsByDiscordId @@ -98,7 +83,7 @@ func (s *Server) ListSubscriptionDetails(w http.ResponseWriter, r *http.Request) // @Success 200 {object} ListSubscriptions "ok" // @Failure 400 {object} ApiError "Unable to reach SQL or Data problems" // @Failure 500 {object} ApiError "Data problems" -func (s *Server) GetSubscriptionsByDiscordId(w http.ResponseWriter, r *http.Request) { +func (s *Handler) GetSubscriptionsByDiscordId(c echo.Context) error { p := ListSubscriptions{ ApiStatusModel: ApiStatusModel{ StatusCode: http.StatusOK, @@ -106,26 +91,24 @@ func (s *Server) GetSubscriptionsByDiscordId(w http.ResponseWriter, r *http.Requ }, } - query := r.URL.Query() - if query["id"][0] == "" { - s.WriteError(w, ErrIdValueMissing, http.StatusBadRequest) - return + id := c.QueryParam("id") + if id == "" { + return s.WriteError(c, ErrIdValueMissing, http.StatusBadRequest) } - uuid, err := uuid.Parse(query["id"][0]) + uuid, err := uuid.Parse(id) if err != nil { - s.WriteError(w, ErrValueNotUuid, http.StatusBadRequest) - return + return s.WriteError(c, ErrValueNotUuid, http.StatusBadRequest) + } - res, err := s.dto.ListSubscriptionsByDiscordWebhookId(r.Context(), uuid) + res, err := s.dto.ListSubscriptionsByDiscordWebhookId(context.Background(), uuid) if err != nil { - s.WriteError(w, err.Error(), http.StatusNoContent) - return + return s.WriteError(c, err.Error(), http.StatusNoContent) } p.Payload = res - s.WriteJson(w, p) + return c.JSON(http.StatusOK, p) } // GetSubscriptionsBySourceId @@ -135,7 +118,7 @@ func (s *Server) GetSubscriptionsByDiscordId(w http.ResponseWriter, r *http.Requ // @Tags Subscription // @Router /subscriptions/by/SourceId [get] // @Success 200 {object} ListSubscriptions "ok" -func (s *Server) GetSubscriptionsBySourceId(w http.ResponseWriter, r *http.Request) { +func (s *Handler) GetSubscriptionsBySourceId(c echo.Context) error { p := ListSubscriptions{ ApiStatusModel: ApiStatusModel{ StatusCode: http.StatusOK, @@ -143,27 +126,23 @@ func (s *Server) GetSubscriptionsBySourceId(w http.ResponseWriter, r *http.Reque }, } - query := r.URL.Query() - _id := query["id"][0] + _id := c.QueryParam("id") if _id == "" { - s.WriteError(w, ErrIdValueMissing, http.StatusBadRequest) - return + return s.WriteError(c, ErrIdValueMissing, http.StatusBadRequest) } uuid, err := uuid.Parse(_id) if err != nil { - s.WriteError(w, err.Error(), http.StatusBadRequest) - return + return s.WriteError(c, err.Error(), http.StatusBadRequest) } - res, err := s.dto.ListSubscriptionsBySourceId(r.Context(), uuid) + res, err := s.dto.ListSubscriptionsBySourceId(context.Background(), uuid) if err != nil { - s.WriteError(w, err.Error(), http.StatusNoContent) - return + return s.WriteError(c, err.Error(), http.StatusNoContent) } p.Payload = res - s.WriteJson(w, p) + return c.JSON(http.StatusOK, p) } // NewDiscordWebHookSubscription @@ -172,42 +151,36 @@ func (s *Server) GetSubscriptionsBySourceId(w http.ResponseWriter, r *http.Reque // @Param sourceId query string true "sourceId" // @Tags Subscription // @Router /subscriptions/discord/webhook/new [post] -func (s *Server) newDiscordWebHookSubscription(w http.ResponseWriter, r *http.Request) { +func (s *Handler) newDiscordWebHookSubscription(c echo.Context) error { // Extract the values given - query := r.URL.Query() - discordWebHookId := query["discordWebHookId"][0] - sourceId := query["sourceId"][0] + discordWebHookId := c.QueryParam("discordWebHookId") + sourceId := c.QueryParam("sourceId") // Check to make we didn't get a null if discordWebHookId == "" { - s.WriteError(w, "invalid discordWebHooksId given", http.StatusBadRequest) - return + return s.WriteError(c, "invalid discordWebHooksId given", http.StatusBadRequest) } if sourceId == "" { - s.WriteError(w, "invalid sourceID given", http.StatusBadRequest) - return + return s.WriteError(c, "invalid sourceID given", http.StatusBadRequest) } // Validate they are UUID values uHook, err := uuid.Parse(discordWebHookId) if err != nil { - s.WriteError(w, err.Error(), http.StatusBadRequest) - return + return s.WriteError(c, err.Error(), http.StatusBadRequest) } uSource, err := uuid.Parse(sourceId) if err != nil { - s.WriteError(w, err.Error(), http.StatusBadRequest) - return + return s.WriteError(c, err.Error(), http.StatusBadRequest) } // Check if the sub already exists - _, err = s.Db.QuerySubscriptions(r.Context(), database.QuerySubscriptionsParams{ + _, err = s.Db.QuerySubscriptions(c.Request().Context(), database.QuerySubscriptionsParams{ Discordwebhookid: uHook, Sourceid: uSource, }) if err == nil { - s.WriteError(w, "a subscription already exists between these two entities", http.StatusBadRequest) - return + return s.WriteError(c, "a subscription already exists between these two entities", http.StatusBadRequest) } // Does not exist, so make it. @@ -216,19 +189,17 @@ func (s *Server) newDiscordWebHookSubscription(w http.ResponseWriter, r *http.Re Discordwebhookid: uHook, Sourceid: uSource, } - err = s.Db.CreateSubscription(r.Context(), params) + err = s.Db.CreateSubscription(context.Background(), params) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return s.WriteError(c, err.Error(), http.StatusInternalServerError) } bJson, err := json.Marshal(¶ms) if err != nil { - s.WriteError(w, err.Error(), http.StatusInternalServerError) - return + return s.WriteError(c, err.Error(), http.StatusInternalServerError) } - w.Write(bJson) + return c.JSON(http.StatusOK, bJson) } // DeleteDiscordWebHookSubscription @@ -236,25 +207,23 @@ func (s *Server) newDiscordWebHookSubscription(w http.ResponseWriter, r *http.Re // @Param id query string true "id" // @Tags Subscription // @Router /subscriptions/discord/webhook/delete [delete] -func (s *Server) DeleteDiscordWebHookSubscription(w http.ResponseWriter, r *http.Request) { +func (s *Handler) DeleteDiscordWebHookSubscription(c echo.Context) error { var ErrMissingSubscriptionID string = "the request was missing a 'Id'" - query := r.URL.Query() - id := query["id"][0] + id := c.QueryParam("id") if id == "" { - s.WriteError(w, ErrMissingSubscriptionID, http.StatusBadRequest) - return + return s.WriteError(c, ErrMissingSubscriptionID, http.StatusBadRequest) } - uid, err := uuid.Parse(query["id"][0]) + uid, err := uuid.Parse(id) if err != nil { - s.WriteError(w, err.Error(), http.StatusBadRequest) - return + return s.WriteError(c, err.Error(), http.StatusBadRequest) } err = s.Db.DeleteSubscription(context.Background(), uid) if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return + return s.WriteError(c, err.Error(), http.StatusInternalServerError) } + + return c.JSON(http.StatusOK, nil) }