diff --git a/internal/database/query.go b/internal/database/query.go new file mode 100644 index 00000000..17e281ae --- /dev/null +++ b/internal/database/query.go @@ -0,0 +1,90 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package database + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "strconv" + + "github.com/jmoiron/sqlx" +) + +type Query struct { + Limit int + Cursor string + InternalPagination + DB *sqlx.DB +} + +// NewQuery handles the logic for generating the pagination token to pass alongside the DB queries for easier access +func (c CLIDatabase) NewQuery(r *http.Request, max_limit int) *Query { + p := Query{DB: c.DB} + if r == nil { + return &p + } + + ic := InternalCursor{} + + query := r.URL.Query() + a := query.Get("after") + f := query.Get("first") + b := query.Get("before") + + isBefore := false + if b != "" { + isBefore = true + } + + if len(a) > 0 { + p.Cursor = a + } + + first, err := strconv.Atoi(f) + if err != nil { + return &p + } + if first > max_limit || first <= 0 { + return &p + } + if first == 0 { + first = 20 + } + p.Limit = int(first) + + if a != "" { + b, err := base64.RawStdEncoding.DecodeString(a) + if err != nil { + return &p + } + err = json.Unmarshal(b, &ic) + if err != nil { + return &p + } + + if isBefore { + ic.Offset -= first + } else { + ic.Offset += first + } + } + + ic.Limit = first + + if ic.Offset < 0 { + return &p + } + + body, _ := json.Marshal(ic) + + ip := InternalPagination{ + InternalCursor: ic, + PaginationCursor: base64.RawURLEncoding.EncodeToString(body), + SQL: fmt.Sprintf(" LIMIT %v OFFSET %v", ic.Limit, ic.Offset), + } + + p.InternalPagination = ip + return &p +} diff --git a/internal/mock_units/categories/categories.go b/internal/mock_units/categories/categories.go new file mode 100644 index 00000000..a05f782e --- /dev/null +++ b/internal/mock_units/categories/categories.go @@ -0,0 +1,42 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package categories + +import ( + "encoding/json" + "net/http" + + "github.com/twitchdev/twitch-cli/internal/database" +) + +type Endpoint struct{} + +var db database.CLIDatabase + +func (e Endpoint) Path() string { return "/categories" } + +func (e Endpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) { + db = r.Context().Value("db").(database.CLIDatabase) + + switch r.Method { + case http.MethodGet: + getCategories(w, r) + break + default: + w.WriteHeader(http.StatusMethodNotAllowed) + } +} + +func getCategories(w http.ResponseWriter, r *http.Request) { + c, err := db.NewQuery(r, 100).GetCategories(database.Category{}) + if err != nil { + w.Write([]byte(err.Error())) + w.WriteHeader(http.StatusInternalServerError) + } + j, err := json.Marshal(c) + if err != nil { + w.Write([]byte(err.Error())) + w.WriteHeader(http.StatusInternalServerError) + } + w.Write(j) +} diff --git a/internal/mock_units/channel_points/channel_points.go b/internal/mock_units/channel_points/channel_points.go new file mode 100644 index 00000000..3f22fb0c --- /dev/null +++ b/internal/mock_units/channel_points/channel_points.go @@ -0,0 +1,27 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package channel_points + +import ( + "net/http" + + "github.com/twitchdev/twitch-cli/internal/database" +) + +type Endpoint struct{} + +var db database.CLIDatabase + +func (e Endpoint) Path() string { return "/categories" } + +func (e Endpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) { + db = r.Context().Value("db").(database.CLIDatabase) + + switch r.Method { + case http.MethodGet: + w.WriteHeader(http.StatusOK) + break + default: + w.WriteHeader(http.StatusMethodNotAllowed) + } +} diff --git a/internal/mock_units/polls/polls.go b/internal/mock_units/polls/polls.go new file mode 100644 index 00000000..c9e65345 --- /dev/null +++ b/internal/mock_units/polls/polls.go @@ -0,0 +1,27 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package mock_units + +import ( + "net/http" + + "github.com/twitchdev/twitch-cli/internal/database" +) + +type Endpoint struct{} + +var db database.CLIDatabase + +func (e Endpoint) Path() string { return "/categories" } + +func (e Endpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) { + db = r.Context().Value("db").(database.CLIDatabase) + + switch r.Method { + case http.MethodGet: + w.WriteHeader(http.StatusOK) + break + default: + w.WriteHeader(http.StatusMethodNotAllowed) + } +} diff --git a/internal/mock_units/predictions/predictions.go b/internal/mock_units/predictions/predictions.go new file mode 100644 index 00000000..28cc1391 --- /dev/null +++ b/internal/mock_units/predictions/predictions.go @@ -0,0 +1,27 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package predictions + +import ( + "net/http" + + "github.com/twitchdev/twitch-cli/internal/database" +) + +type Endpoint struct{} + +var db database.CLIDatabase + +func (e Endpoint) Path() string { return "/categories" } + +func (e Endpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) { + db = r.Context().Value("db").(database.CLIDatabase) + + switch r.Method { + case http.MethodGet: + w.WriteHeader(http.StatusOK) + break + default: + w.WriteHeader(http.StatusMethodNotAllowed) + } +} diff --git a/internal/mock_units/streams/streams.go b/internal/mock_units/streams/streams.go new file mode 100644 index 00000000..64fed15a --- /dev/null +++ b/internal/mock_units/streams/streams.go @@ -0,0 +1,42 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package streams + +import ( + "encoding/json" + "net/http" + + "github.com/twitchdev/twitch-cli/internal/database" +) + +type Endpoint struct{} + +var db database.CLIDatabase + +func (e Endpoint) Path() string { return "/streams" } + +func (e Endpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) { + db = r.Context().Value("db").(database.CLIDatabase) + + switch r.Method { + case http.MethodGet: + getStreams(w, r) + break + default: + w.WriteHeader(http.StatusMethodNotAllowed) + } +} + +func getStreams(w http.ResponseWriter, r *http.Request) { + s, err := db.NewQuery(r, 100).GetStream(database.Stream{}) + if err != nil { + w.Write([]byte(err.Error())) + w.WriteHeader(http.StatusInternalServerError) + } + j, err := json.Marshal(s) + if err != nil { + w.Write([]byte(err.Error())) + w.WriteHeader(http.StatusInternalServerError) + } + w.Write(j) +} diff --git a/internal/mock_units/subscriptions/subscriptions.go b/internal/mock_units/subscriptions/subscriptions.go new file mode 100644 index 00000000..bb74a876 --- /dev/null +++ b/internal/mock_units/subscriptions/subscriptions.go @@ -0,0 +1,42 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package subscriptions + +import ( + "encoding/json" + "net/http" + + "github.com/twitchdev/twitch-cli/internal/database" +) + +type Endpoint struct{} + +var db database.CLIDatabase + +func (e Endpoint) Path() string { return "/subscriptions" } + +func (e Endpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) { + db = r.Context().Value("db").(database.CLIDatabase) + + switch r.Method { + case http.MethodGet: + getStreams(w, r) + break + default: + w.WriteHeader(http.StatusMethodNotAllowed) + } +} + +func getStreams(w http.ResponseWriter, r *http.Request) { + s, err := db.NewQuery(r, 100).GetSubscriptions(database.Subscription{}) + if err != nil { + w.Write([]byte(err.Error())) + w.WriteHeader(http.StatusInternalServerError) + } + j, err := json.Marshal(s) + if err != nil { + w.Write([]byte(err.Error())) + w.WriteHeader(http.StatusInternalServerError) + } + w.Write(j) +} diff --git a/internal/mock_units/tags/tags.go b/internal/mock_units/tags/tags.go new file mode 100644 index 00000000..57dba97b --- /dev/null +++ b/internal/mock_units/tags/tags.go @@ -0,0 +1,42 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package tags + +import ( + "encoding/json" + "net/http" + + "github.com/twitchdev/twitch-cli/internal/database" +) + +type Endpoint struct{} + +var db database.CLIDatabase + +func (e Endpoint) Path() string { return "/tags" } + +func (e Endpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) { + db = r.Context().Value("db").(database.CLIDatabase) + + switch r.Method { + case http.MethodGet: + getTags(w, r) + break + default: + w.WriteHeader(http.StatusMethodNotAllowed) + } +} + +func getTags(w http.ResponseWriter, r *http.Request) { + s, err := db.NewQuery(r, 100).GetTags(database.Tag{}) + if err != nil { + w.Write([]byte(err.Error())) + w.WriteHeader(http.StatusInternalServerError) + } + j, err := json.Marshal(s) + if err != nil { + w.Write([]byte(err.Error())) + w.WriteHeader(http.StatusInternalServerError) + } + w.Write(j) +} diff --git a/internal/mock_units/teams/teams.go b/internal/mock_units/teams/teams.go new file mode 100644 index 00000000..32563d88 --- /dev/null +++ b/internal/mock_units/teams/teams.go @@ -0,0 +1,42 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package teams + +import ( + "encoding/json" + "net/http" + + "github.com/twitchdev/twitch-cli/internal/database" +) + +type Endpoint struct{} + +var db database.CLIDatabase + +func (e Endpoint) Path() string { return "/teams" } + +func (e Endpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) { + db = r.Context().Value("db").(database.CLIDatabase) + + switch r.Method { + case http.MethodGet: + getTeams(w, r) + break + default: + w.WriteHeader(http.StatusMethodNotAllowed) + } +} + +func getTeams(w http.ResponseWriter, r *http.Request) { + u, err := db.NewQuery(r, 100).GetTeam(database.Team{}) + if err != nil { + w.Write([]byte(err.Error())) + w.WriteHeader(http.StatusInternalServerError) + } + j, err := json.Marshal(u) + if err != nil { + w.Write([]byte(err.Error())) + w.WriteHeader(http.StatusInternalServerError) + } + w.Write(j) +} diff --git a/internal/mock_units/units.go b/internal/mock_units/units.go new file mode 100644 index 00000000..a5e65871 --- /dev/null +++ b/internal/mock_units/units.go @@ -0,0 +1,33 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package mock_units + +import ( + "net/http" + + "github.com/twitchdev/twitch-cli/internal/mock_units/categories" + "github.com/twitchdev/twitch-cli/internal/mock_units/streams" + "github.com/twitchdev/twitch-cli/internal/mock_units/subscriptions" + "github.com/twitchdev/twitch-cli/internal/mock_units/tags" + "github.com/twitchdev/twitch-cli/internal/mock_units/teams" + "github.com/twitchdev/twitch-cli/internal/mock_units/users" + "github.com/twitchdev/twitch-cli/internal/mock_units/videos" +) + +// MockEndpoint is an implementation of an endpoint in the API; this enables the quick building of new endpoints with minimal additional logic +type UnitEndpoint interface { + Path() string + ServeHTTP(w http.ResponseWriter, r *http.Request) +} + +func All() []UnitEndpoint { + return []UnitEndpoint{ + categories.Endpoint{}, + users.Endpoint{}, + teams.Endpoint{}, + videos.Endpoint{}, + streams.Endpoint{}, + tags.Endpoint{}, + subscriptions.Endpoint{}, + } +} diff --git a/internal/mock_units/users/users.go b/internal/mock_units/users/users.go new file mode 100644 index 00000000..d41c4d75 --- /dev/null +++ b/internal/mock_units/users/users.go @@ -0,0 +1,42 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package users + +import ( + "encoding/json" + "net/http" + + "github.com/twitchdev/twitch-cli/internal/database" +) + +type Endpoint struct{} + +var db database.CLIDatabase + +func (e Endpoint) Path() string { return "/users" } + +func (e Endpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) { + db = r.Context().Value("db").(database.CLIDatabase) + + switch r.Method { + case http.MethodGet: + getUsers(w, r) + break + default: + w.WriteHeader(http.StatusMethodNotAllowed) + } +} + +func getUsers(w http.ResponseWriter, r *http.Request) { + u, err := db.NewQuery(r, 100).GetUsers(database.User{}) + if err != nil { + w.Write([]byte(err.Error())) + w.WriteHeader(http.StatusInternalServerError) + } + j, err := json.Marshal(u) + if err != nil { + w.Write([]byte(err.Error())) + w.WriteHeader(http.StatusInternalServerError) + } + w.Write(j) +} diff --git a/internal/mock_units/videos/videos.go b/internal/mock_units/videos/videos.go new file mode 100644 index 00000000..580a6f3d --- /dev/null +++ b/internal/mock_units/videos/videos.go @@ -0,0 +1,42 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package videos + +import ( + "encoding/json" + "net/http" + + "github.com/twitchdev/twitch-cli/internal/database" +) + +type Endpoint struct{} + +var db database.CLIDatabase + +func (e Endpoint) Path() string { return "/videos" } + +func (e Endpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) { + db = r.Context().Value("db").(database.CLIDatabase) + + switch r.Method { + case http.MethodGet: + getVideos(w, r) + break + default: + w.WriteHeader(http.StatusMethodNotAllowed) + } +} + +func getVideos(w http.ResponseWriter, r *http.Request) { + u, err := db.NewQuery(r, 100).GetVideos(database.Video{}) + if err != nil { + w.Write([]byte(err.Error())) + w.WriteHeader(http.StatusInternalServerError) + } + j, err := json.Marshal(u) + if err != nil { + w.Write([]byte(err.Error())) + w.WriteHeader(http.StatusInternalServerError) + } + w.Write(j) +}