Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
792 changes: 791 additions & 1 deletion docs/openapi.json

Large diffs are not rendered by default.

109 changes: 94 additions & 15 deletions gists/controller.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package gists

import (
"errors"
"strconv"

"github.com/gistapp/api/utils"
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/log"
Expand All @@ -13,20 +15,40 @@ type GistControllerImpl struct {
}

type GistSaveValidator struct {
Name string `json:"name" validate:"required"`
Content string `json:"content" validate:"required"`
OrgID string `json:"org_id,omitempty"`
Language string `json:"language,omitempty"`
Description string `json:"description,omitempty"`
Visibility string `json:"visibility,omitempty"`
Name string `json:"name" validate:"required"`
Content string `json:"content" validate:"required"`
OrgID utils.ZeroString `json:"org_id,omitempty"`
Language string `json:"language,omitempty"`
Description string `json:"description,omitempty"`
Visibility string `json:"visibility,omitempty"`
}

type GistUpdateValidator struct {
Name string `json:"name" validate:"required"`
Content string `json:"content" validate:"required"`
OrgID utils.ZeroString `json:"org_id,omitempty"`
Language string `json:"language,omitempty"`
Description string `json:"description,omitempty"`
Visibility string `json:"visibility,omitempty"`
}

func defaultGist() *GistSaveValidator {
return &GistSaveValidator{
Language: "plaintext",
Visibility: "public",
Description: "",
}

}

func (g *GistControllerImpl) Save() fiber.Handler {
return func(c *fiber.Ctx) error {
g := new(GistSaveValidator)
g := defaultGist()
owner_id := c.Locals("pub").(string)
log.Info(owner_id)

if err := c.BodyParser(g); err != nil {
log.Info(err)
return c.Status(400).SendString("Request must be valid JSON with fields name and content as text")
}
validate := validator.New(validator.WithRequiredStructEnabled())
Expand All @@ -38,17 +60,18 @@ func (g *GistControllerImpl) Save() fiber.Handler {
return c.Status(400).SendString("Request must be valid JSON with fields name and content as text")
}

visibility := "public"
if g.Visibility == "private" {
visibility = "private"
if g.Visibility != "public" && g.Visibility != "private" {
return c.Status(400).SendString("Visibility must be either public or private")
}
log.Info(g)

gist, err := GistService.Save(g.Name, g.Content, owner_id, g.OrgID, g.Language, g.Description, visibility)
gist, err := GistService.Save(g.Name, g.Content, owner_id, g.OrgID.SqlString(), g.Language, g.Description, g.Visibility)
if err != nil {
log.Error(err)
return c.Status(500).SendString(err.Error())
}

return c.Status(201).JSON(gist)
return c.Status(201).JSON(gist.ToJSON())
}
}

Expand Down Expand Up @@ -78,7 +101,7 @@ func (g *GistControllerImpl) UpdateName() fiber.Handler {
}
return c.Status(400).SendString(err.Error()) //could be because gist not found
}
return c.Status(200).JSON(gist)
return c.Status(200).JSON(gist.ToJSON())
}
}

Expand Down Expand Up @@ -115,8 +138,15 @@ func (g *GistControllerImpl) FindAll() fiber.Handler {
return c.Status(500).SendString(err.Error())
}

gists_json := make([]map[string]interface{}, 0)

for _, gist := range gists {
gists_json = append(gists_json, gist.ToJSON())

}

return c.JSON(map[string]interface{}{
"gists": gists,
"gists": gists_json,
"nb_pages": nb_pages,
})
}
Expand Down Expand Up @@ -205,7 +235,7 @@ func (g *GistControllerImpl) UpdateContent() fiber.Handler {

func (g *GistControllerImpl) UpdateLanguage() fiber.Handler {
return func(c *fiber.Ctx) error {
g_body := new(GistSaveValidator)
g_body := defaultGist()
if err := c.BodyParser(g_body); err != nil {
return c.Status(400).SendString("Request must be valid JSON with fields name and content as text")
}
Expand Down Expand Up @@ -285,4 +315,53 @@ func (g *GistControllerImpl) Delete() fiber.Handler {
}
}

func (g *GistControllerImpl) Update() fiber.Handler {
return func(c *fiber.Ctx) error {
gist_validate, err := validateGist(c)
id := c.Params("id")
owner_id := c.Locals("pub").(string)
if err != nil {
return c.Status(400).SendString(err.Error())
}

can_edit, err := g.gist_guard.CanEdit(id, owner_id)

if err != nil {
return c.Status(500).SendString(err.Error())
}

if !can_edit {
return c.Status(403).SendString("You do not have permission to edit this gist")
}

gist, err := GistService.Update(id, gist_validate.Name, gist_validate.OrgID, gist_validate.Content, gist_validate.Language, gist_validate.Description, gist_validate.Visibility, owner_id)
return c.Status(200).JSON(gist.ToJSON())
}
}

func validateGist(c *fiber.Ctx) (*GistSaveValidator, error) {
g := defaultGist()
owner_id := c.Locals("pub").(string)
log.Info(owner_id)

if err := c.BodyParser(g); err != nil {
log.Info(err)
return nil, err
}
validate := validator.New(validator.WithRequiredStructEnabled())

err := validate.Struct(g)

if err != nil {
log.Error(err)
return nil, err
}

if g.Visibility != "public" && g.Visibility != "private" {
return nil, errors.New("Visibility must be either public or private")
}

return g, nil
}

var GistController GistControllerImpl = GistControllerImpl{}
92 changes: 70 additions & 22 deletions gists/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"

"github.com/gistapp/api/storage"
"github.com/gistapp/api/utils"
"github.com/gistsapp/pogo/pogo"
"github.com/gofiber/fiber/v2/log"
)
Expand All @@ -17,51 +18,78 @@ const (
)

type GistSQL struct {
ID sql.NullString
Name sql.NullString
Content sql.NullString
OwnerID sql.NullString
ID string
Name string
Content string
OwnerID string
OrgID sql.NullString
Description sql.NullString
Language sql.NullString
Visibility sql.NullString
Description string
Language string
Visibility string
}

type Gist struct {
ID string `json:"id" pogo:"gist_id"`
Name string `json:"name" pogo:"name"`
Content string `json:"content" pogo:"content"`
OwnerID string `json:"owner_id" pogo:"owner"`
OrgID *string `json:"org_id,omitempty" pogo:"org_id"`
Description string `json:"description" pogo:"description"`
Language string `json:"language" pogo:"language"`
Visibility string `json:"visibility" pogo:"visibility"`
ID string `json:"id" pogo:"gist_id"`
Name string `json:"name" pogo:"name"`
Content string `json:"content" pogo:"content"`
OwnerID string `json:"owner_id" pogo:"owner"`
OrgID sql.NullString `json:"org_id,omitempty" pogo:"org_id"`
Description string `json:"description" pogo:"description"`
Language string `json:"language" pogo:"language"`
Visibility string `json:"visibility" pogo:"visibility"`
}

type GistModel interface {
Save() error
}

func (g *Gist) ToJSON() map[string]interface{} {
return map[string]interface{}{
"id": g.ID,
"name": g.Name,
"content": g.Content,
"owner_id": g.OwnerID,
"org_id": utils.ToNullString(g.OrgID),
"description": g.Description,
"language": g.Language,
"visibility": g.Visibility,
}
}

func NewGistSQL(ID string, Name string, Content string, OwnerID string, OrgID sql.NullString, Description string, Language string, Visibility string) *GistSQL {
return &GistSQL{
ID: ID,
Name: Name,
Content: Content,
OwnerID: OwnerID,
OrgID: OrgID,
Description: Description,
Language: Language,
Visibility: Visibility,
}
}

func (g *GistSQL) Save() (*Gist, error) {
db := storage.PogoDatabase
gists := make([]Gist, 0)
err := pogo.SuperQuery(db, "INSERT INTO gists(name, content, owner, org_id, language, description, visibility) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING :fields", &gists, g.Name.String, g.Content.String, g.OwnerID.String, g.OrgID, g.Language, g.Description, g.Visibility.String)
err := pogo.SuperQuery(db, "INSERT INTO gists(name, content, owner, org_id, language, description, visibility) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING :fields", &gists, g.Name, g.Content, g.OwnerID, g.OrgID, g.Language, g.Description, g.Visibility)

if len(gists) <= 0 {
log.Error(err)
return nil, errors.New("couldn't create gist")
}
return &gists[0], err
}

func (g *GistSQL) UpdateName(id string) (*Gist, error) {
return g.UpdateField(id, "name", g.Name.String)
return g.UpdateField(id, "name", g.Name)
}

func (g *GistSQL) UpdateContent(id string) (*Gist, error) {
db := storage.PogoDatabase

gists := make([]Gist, 0)
err := pogo.SuperQuery(db, "UPDATE gists SET content = $1 WHERE gist_id = $2 AND owner = $3 RETURNING :fields", &gists, g.Content.String, id, g.OwnerID.String)
err := pogo.SuperQuery(db, "UPDATE gists SET content = $1 WHERE gist_id = $2 AND owner = $3 RETURNING :fields", &gists, g.Content, id, g.OwnerID)

if len(gists) <= 0 {
return nil, errors.New("gist not found")
Expand All @@ -73,7 +101,7 @@ func (g *GistSQL) UpdateContent(id string) (*Gist, error) {
func (g *GistSQL) UpdateField(id string, field string, val string) (*Gist, error) {
db := storage.PogoDatabase
gists := make([]Gist, 0)
err := pogo.SuperQuery(db, "UPDATE gists SET "+field+" = $1 WHERE gist_id = $2 AND owner = $3 RETURNING :fields", &gists, val, id, g.OwnerID.String)
err := pogo.SuperQuery(db, "UPDATE gists SET "+field+" = $1 WHERE gist_id = $2 AND owner = $3 RETURNING :fields", &gists, val, id, g.OwnerID)
if len(gists) <= 0 {
return nil, errors.New("gist not found")
}
Expand All @@ -84,8 +112,18 @@ func (g *GistSQL) UpdateVisibility(id string, visibility string) (*Gist, error)
return g.UpdateField(id, "visibility", visibility)
}

func (g *GistSQL) UpdateGist() (*Gist, error) {
db := storage.PogoDatabase
gists := make([]Gist, 0)
err := pogo.SuperQuery(db, "UPDATE gists SET name = $1, content = $2, language = $3, description = $4, visibility = $5 WHERE gist_id = $6 AND owner = $7 RETURNING :fields", &gists, g.Name, g.Content, g.Language, g.Description, g.Visibility, g.ID, g.OwnerID)
if len(gists) <= 0 {
return nil, errors.New("gist not found")
}
return &gists[0], err
}

func (g *GistSQL) Delete(id string) error {
_, err := storage.Database.Exec("DELETE FROM gists WHERE gist_id = $1 AND owner = $2", id, g.OwnerID.String)
_, err := storage.Database.Exec("DELETE FROM gists WHERE gist_id = $1 AND owner = $2", id, g.OwnerID)
if err != nil {
log.Error(err)
return errors.New("couldn't delete gist")
Expand All @@ -109,18 +147,28 @@ func (g *GistSQL) FindAll(limit int, offset int) ([]Gist, error) {
db := storage.PogoDatabase

gists := make([]Gist, 0)
err := pogo.SuperQuery(db, "SELECT :fields FROM gists WHERE owner = $1 LIMIT $2 OFFSET $3", &gists, g.OwnerID.String, limit, offset)
err := pogo.SuperQuery(db, "SELECT :fields FROM gists WHERE owner = $1 LIMIT $2 OFFSET $3", &gists, g.OwnerID, limit, offset)
if len(gists) <= 0 {
log.Error(err)
return nil, errors.New("gist not found")
}
return gists, err
}

func (g *GistSQL) Update() (*Gist, error) {
db := storage.PogoDatabase
gists := make([]Gist, 0)
err := pogo.SuperQuery(db, "UPDATE gists SET name = $1, content = $2, language = $3, description = $4, visibility = $5 WHERE gist_id = $6 AND owner = $7 RETURNING :fields", &gists, g.Name, g.Content, g.Language, g.Description, g.Visibility, g.ID, g.OwnerID)
if len(gists) <= 0 {
return nil, errors.New("gist not found")
}
return &gists[0], err
}

func (g *GistSQL) Count() (int, error) {
db := storage.PogoDatabase
var count int
rows, err := db.Query("SELECT COUNT(*) FROM gists WHERE owner = $1", g.OwnerID.String)
rows, err := db.Query("SELECT COUNT(*) FROM gists WHERE owner = $1", g.OwnerID)

rows.Next()

Expand Down
1 change: 1 addition & 0 deletions gists/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func (r *GistRouter) SubscribeRoutes(app *fiber.Router) {
gists_router.Patch("/:id/content", r.Controller.UpdateContent())
gists_router.Patch("/:id/language", r.Controller.UpdateLanguage())
gists_router.Patch("/:id/description", r.Controller.UpdateDescription())
gists_router.Put("/:id", r.Controller.Update())
gists_router.Get("/", r.Controller.FindAll())
gists_router.Get("/:id", r.Controller.FindByID())
gists_router.Get("/raw/:id", r.Controller.RawFindByID())
Expand Down
Loading