From 64d15c4621b073fd7e0a6df7150fbff4f7aef70c Mon Sep 17 00:00:00 2001 From: dragongoose <19649813+dragongoose@users.noreply.github.com> Date: Sat, 20 May 2023 22:18:10 -0400 Subject: [PATCH] Add global error handler, make progress on the first endpoint --- extractor/structs/parsed.go | 7 +++++++ extractor/structs/streamerInfoRaw.go | 14 ++++++++++++++ extractor/twitchExtractor.go | 22 +++++++++++++++++++--- extractor/utils.go | 26 ++++++++++++++++++++++++++ go.mod | 4 ++++ main.go | 15 +++++++++++++++ routes/users/users.go | 15 +++++++++++++-- 7 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 extractor/structs/parsed.go create mode 100644 extractor/utils.go diff --git a/extractor/structs/parsed.go b/extractor/structs/parsed.go new file mode 100644 index 0000000..daa3ee4 --- /dev/null +++ b/extractor/structs/parsed.go @@ -0,0 +1,7 @@ +package structs + +type Social struct { + Type string + Title string + Url string +} diff --git a/extractor/structs/streamerInfoRaw.go b/extractor/structs/streamerInfoRaw.go index e1859b4..ee11e0c 100644 --- a/extractor/structs/streamerInfoRaw.go +++ b/extractor/structs/streamerInfoRaw.go @@ -1,5 +1,11 @@ package structs +// API RESPONSE + +// To save space and readability, i'm taking this approach +// to fit to any response from twitch, and then define the +// specific parts later, because we don't need every part +// of the request type TwitchApiResponse struct { Data map[string]interface{} `json:"data"` Extensions Extensions `json:"extensions"` @@ -9,3 +15,11 @@ type Extensions struct { OperationName string `json:"operationName"` RequestID string `json:"requestID"` } + +// PARTS OF RESPONSE + +type RawSocial struct { + Name string `json:"name"` + Title string `json:"title"` + Url string `json:"url"` +} diff --git a/extractor/twitchExtractor.go b/extractor/twitchExtractor.go index 2dd6b3e..67e4eaa 100644 --- a/extractor/twitchExtractor.go +++ b/extractor/twitchExtractor.go @@ -3,11 +3,14 @@ package extractor import ( "bytes" "encoding/json" + "errors" "fmt" "io" "log" "net/http" "safetwitch-backend/extractor/structs" + + "github.com/tidwall/gjson" ) const twitchUrl = "https://gql.twitch.tv/gql" @@ -25,7 +28,7 @@ func call(url, method string, body io.Reader) (*http.Response, error) { type TwitchPayload = map[string]interface{} -func GetStreamerInfo(streamerName string) structs.StreamerDataRaw { +func GetStreamerInfo(streamerName string) (structs.TwitchApiResponse, error) { payload := []TwitchPayload{ { "operationName": "ChannelRoot_AboutPanel", @@ -101,11 +104,24 @@ func GetStreamerInfo(streamerName string) structs.StreamerDataRaw { } body, err := io.ReadAll(resp.Body) - var result []structs.StreamerDataRaw + var result []structs.TwitchApiResponse err = json.Unmarshal(body, &result) if err != nil { log.Fatalln(err) } - return result[0] + // begin parsing response + streamerFound := gjson.Get(string(body), "0.data.user") + if streamerFound.String() == "" { + return result[0], errors.New("streamer not found") + } + + streamerData := gjson.Get(string(body), "0.data") + parsedSocials, err := ParseSocials(streamerData.String()) + if err != nil { + return result[0], nil + } + log.Println(parsedSocials) + + return result[0], nil } diff --git a/extractor/utils.go b/extractor/utils.go new file mode 100644 index 0000000..ff52b60 --- /dev/null +++ b/extractor/utils.go @@ -0,0 +1,26 @@ +package extractor + +import ( + "errors" + "safetwitch-backend/extractor/structs" + + "github.com/tidwall/gjson" +) + +func ParseSocials(data string) ([]structs.Social, error) { + var parsedSocials []structs.Social + result := gjson.Get(data, "user.channel.socialMedias") + for _, social := range result.Array() { + parsedSocials = append(parsedSocials, structs.Social{ + Title: social.Get("title").String(), + Type: social.Get("name").String(), + Url: social.Get("url").String(), + }) + } + + if !result.Exists() { + return parsedSocials, errors.New("Error while parsing socials, path does not exist.") + } + + return parsedSocials, nil +} diff --git a/go.mod b/go.mod index d891177..14a2a01 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.20 require github.com/gin-gonic/gin v1.9.0 require ( + github.com/buger/jsonparser v1.1.1 // indirect github.com/bytedance/sonic v1.8.8 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/gin-contrib/sse v0.1.0 // indirect @@ -19,6 +20,9 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.0.7 // indirect + github.com/tidwall/gjson v1.14.4 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect golang.org/x/arch v0.3.0 // indirect diff --git a/main.go b/main.go index 3db1795..7c3896d 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "safetwitch-backend/routes" "strings" @@ -8,6 +9,19 @@ import ( "github.com/gin-gonic/gin" ) +func ErrorHandler(c *gin.Context) { + c.Next() + + err := c.Errors.Last() + if err != nil { + c.JSON(500, gin.H{ + "status": "error", + "message": err.Error(), + }) + } + fmt.Println(err) +} + func notFoundHandler(context *gin.Context) { message := []string{"path", context.Request.URL.Path, "was not found."} @@ -19,6 +33,7 @@ func notFoundHandler(context *gin.Context) { func main() { router := gin.Default() + router.Use(ErrorHandler) routes.SetRoutes(router) router.NoRoute(notFoundHandler) diff --git a/routes/users/users.go b/routes/users/users.go index fed0043..ea28f01 100644 --- a/routes/users/users.go +++ b/routes/users/users.go @@ -10,7 +10,18 @@ func Routes(route *gin.Engine) { auth := route.Group("/users") auth.GET("/:streamerName", func(context *gin.Context) { - data := extractor.GetStreamerInfo(context.Param("streamerName")) - context.JSON(200, data) + data, err := extractor.GetStreamerInfo(context.Param("streamerName")) + if err != nil { + if err.Error() == "streamer not found" { + context.JSON(404, gin.H{ + "status": "error", + "message": "streamer not found", + }) + } else { + context.Error(err) + } + } else { + context.JSON(200, data) + } }) }