Skip to content

Commit

Permalink
GPTUBE-66 Create endpoint for dashboard videos (#67)
Browse files Browse the repository at this point in the history
* feat: adding videos endpoint and its documentation

* feat: adding database query for getting data

* feat: added final logic for the paginated query of videos
  • Loading branch information
webtaken authored Oct 26, 2023
1 parent 004abe4 commit b6240d4
Show file tree
Hide file tree
Showing 16 changed files with 727 additions and 20 deletions.
1 change: 1 addition & 0 deletions go-server/.env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Local Configurations
APP_BASE_URL="" # GPTube api base url
EMAIL_USER="" # GPTube e-mail account address
EMAIL_PASSWORD="" # GPTube e-mail account password
FRONTEND_URL="" # FrontendURL
Expand Down
109 changes: 97 additions & 12 deletions go-server/database/firebase.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ package database

import (
"context"
"errors"
"fmt"
"gptube/config"
"gptube/models"
"log"
"math"
"time"

"cloud.google.com/go/firestore"
"cloud.google.com/go/firestore/apiv1/firestorepb"
firebase "firebase.google.com/go"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
Expand All @@ -27,6 +30,20 @@ func init() {
}
}

func CountCollection(collection *firestore.CollectionRef) (int, error) {
aggQuery := collection.NewAggregationQuery().WithCount("all")
res, err := aggQuery.Get(Ctx)
if err != nil {
return 0, err
}
count, ok := res["all"]
if !ok {
return 0, errors.New("error while getting the count of the model")
}
countValue := count.(*firestorepb.Value)
return int(countValue.GetIntegerValue()), nil
}

func GetClient() (*firestore.Client, error) {
app, err := firebase.NewApp(Ctx, nil, Sa)
if err != nil {
Expand Down Expand Up @@ -132,21 +149,89 @@ func AddYoutubeResult(results *models.YoutubeAnalyzerRespBody) error {
}
}

// aggregationQuery := negativeCommentsColl.NewAggregationQuery().WithCount("all")
// res, err := aggregationQuery.Get(Ctx)
// if err != nil {
// return err
// }

// count, ok := res["all"]
// if !ok {
// return errors.New("firestore: couldn't get alias for COUNT from results")
// }
// countValue := count.(*firestorepb.Value)
// fmt.Printf("Number of Negative Comments after insertions: %d\n", countValue.GetIntegerValue())
return nil
}

func GetYoutubeVideosPage(page int, pageSize int, accountEmail string) (*models.YoutubeVideosRespBody, error) {
app, err := firebase.NewApp(Ctx, nil, Sa)
if err != nil {
return nil, err
}

client, err := app.Firestore(Ctx)
if err != nil {
return nil, err
}

defer client.Close()

response := &models.YoutubeVideosRespBody{
Count: 0,
Next: nil,
Previous: nil,
Results: make([]models.YoutubeVideoDashboard, 0),
}

userDoc := client.Collection("users").Doc(accountEmail)
youtubeColl := userDoc.Collection("youtube")

// Query the page
query := youtubeColl.OrderBy("last_update", firestore.Desc).Limit(pageSize)
if page > 1 {
prevPageSnapshot, err := youtubeColl.OrderBy("last_update", firestore.Desc).
Limit(pageSize * (page - 1)).Documents(Ctx).GetAll()
if err != nil {
return nil, err
}
lastDoc := prevPageSnapshot[len(prevPageSnapshot)-1]
query = youtubeColl.OrderBy("last_update", firestore.Desc).
StartAfter(lastDoc.Data()["last_update"]).Limit(pageSize)
}
pageSnapshot, err := query.Documents(Ctx).GetAll()
if err != nil {
return nil, err
}
if len(pageSnapshot) == 0 {
return nil, errors.New("could not receive any result")
}

// Count all the youtube videos in the collection
response.Count, err = CountCollection(youtubeColl)
if err != nil {
return nil, err
}

// Filling the next and previous fields
totalPages := int(math.Ceil(float64(response.Count) / float64(pageSize)))
baseURL := config.Config("APP_BASE_URL")
prevPageVal := fmt.Sprintf("%s/api/youtube/videos?account_email=%s&page=%v&page_size=%v",
baseURL, accountEmail, page-1, pageSize)
nextPageVal := fmt.Sprintf("%s/api/youtube/videos?account_email=%s&page=%v&page_size=%v",
baseURL, accountEmail, page+1, pageSize)

if 1 <= page && page <= totalPages {
response.Previous = &prevPageVal
response.Next = &nextPageVal
if page == 1 {
response.Previous = nil
}
if page == totalPages {
response.Next = nil
}
} else {
return nil, errors.New("page range error")
}

// Filling the results array
for _, snapshot := range pageSnapshot {
var videoResult models.YoutubeVideoDashboard
snapshot.DataTo(&videoResult)
response.Results = append(response.Results, videoResult)
}

return response, nil
}

func RetrieveSubscriptions(email string) (*[]map[string]interface{}, error) {
app, err := firebase.NewApp(Ctx, nil, Sa)
if err != nil {
Expand Down
92 changes: 91 additions & 1 deletion go-server/docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,56 @@ const docTemplate = `{
}
}
},
"/api/youtube/videos": {
"get": {
"description": "An endpoint to retrieve all the youtube videos that a user has analyzed, results are sorted by last_update field.",
"produces": [
"application/json"
],
"summary": "Get all the videos related to a user in paginated mode",
"parameters": [
{
"type": "string",
"description": "the account email",
"name": "account_email",
"in": "query",
"required": true
},
{
"type": "integer",
"description": "the queried page",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "page size for the results (default: 10, max: 50)",
"name": "page_size",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/models.YoutubeVideosRespBody"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/utils.HandleError.errorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/utils.HandleError.errorResponse"
}
}
}
}
},
"/billing": {
"get": {
"description": "An endpoint used to test the billing api stability",
Expand Down Expand Up @@ -333,6 +383,46 @@ const docTemplate = `{
}
}
},
"models.YoutubeVideoDashboard": {
"type": "object",
"properties": {
"created_at": {
"type": "string"
},
"last_update": {
"type": "string"
},
"snippet": {
"$ref": "#/definitions/youtube.VideoSnippet"
},
"video_id": {
"type": "string"
}
}
},
"models.YoutubeVideosRespBody": {
"type": "object",
"properties": {
"count": {
"type": "integer",
"example": 10
},
"next": {
"type": "string",
"example": "http://example.com"
},
"previous": {
"type": "string",
"example": "http://example.com"
},
"results": {
"type": "array",
"items": {
"$ref": "#/definitions/models.YoutubeVideoDashboard"
}
}
}
},
"utils.HandleError.errorResponse": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -517,7 +607,7 @@ const docTemplate = `{
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{
Version: "1.0",
Host: "localhost:8000",
Host: "localhost:8001",
BasePath: "/api",
Schemes: []string{},
Title: "GPTube API swagger docs",
Expand Down
92 changes: 91 additions & 1 deletion go-server/docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
},
"version": "1.0"
},
"host": "localhost:8000",
"host": "localhost:8001",
"basePath": "/api",
"paths": {
"/api": {
Expand Down Expand Up @@ -166,6 +166,56 @@
}
}
},
"/api/youtube/videos": {
"get": {
"description": "An endpoint to retrieve all the youtube videos that a user has analyzed, results are sorted by last_update field.",
"produces": [
"application/json"
],
"summary": "Get all the videos related to a user in paginated mode",
"parameters": [
{
"type": "string",
"description": "the account email",
"name": "account_email",
"in": "query",
"required": true
},
{
"type": "integer",
"description": "the queried page",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "page size for the results (default: 10, max: 50)",
"name": "page_size",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/models.YoutubeVideosRespBody"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/utils.HandleError.errorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/utils.HandleError.errorResponse"
}
}
}
}
},
"/billing": {
"get": {
"description": "An endpoint used to test the billing api stability",
Expand Down Expand Up @@ -327,6 +377,46 @@
}
}
},
"models.YoutubeVideoDashboard": {
"type": "object",
"properties": {
"created_at": {
"type": "string"
},
"last_update": {
"type": "string"
},
"snippet": {
"$ref": "#/definitions/youtube.VideoSnippet"
},
"video_id": {
"type": "string"
}
}
},
"models.YoutubeVideosRespBody": {
"type": "object",
"properties": {
"count": {
"type": "integer",
"example": 10
},
"next": {
"type": "string",
"example": "http://example.com"
},
"previous": {
"type": "string",
"example": "http://example.com"
},
"results": {
"type": "array",
"items": {
"$ref": "#/definitions/models.YoutubeVideoDashboard"
}
}
}
},
"utils.HandleError.errorResponse": {
"type": "object",
"properties": {
Expand Down
Loading

1 comment on commit b6240d4

@vercel
Copy link

@vercel vercel bot commented on b6240d4 Oct 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

gptube-subscriptions – ./subscriptions-server

gptube-subscriptions.vercel.app
gptube-subscriptions-luckly083-gmailcom.vercel.app
gptube-subscriptions-git-main-luckly083-gmailcom.vercel.app

Please sign in to comment.