Unified upload, edit and delete endpoint (new api specs)
This commit is contained in:
@@ -15,6 +15,95 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func Article(w http.ResponseWriter, r *http.Request) {
|
||||||
|
switch r.Method {
|
||||||
|
case http.MethodGet:
|
||||||
|
assetsGet(w, r)
|
||||||
|
case http.MethodPost:
|
||||||
|
assetsPost(w, r)
|
||||||
|
case http.MethodDelete:
|
||||||
|
assetsDelete(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type uploadPayload struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Summary string `json:"summary"`
|
||||||
|
Authors []int `json:"authors"`
|
||||||
|
Image string `json:"image"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
Link string `json:"link"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func articlePost(w http.ResponseWriter, r *http.Request) {
|
||||||
|
authorId, ok := authorizedSession(r)
|
||||||
|
if !ok {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var payload uploadPayload
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||||
|
InvalidJson.Send(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rawMarkdown, err := base64.StdEncoding.DecodeString(payload.Content)
|
||||||
|
if err != nil {
|
||||||
|
zap.S().Warnf("Cannot decode base64")
|
||||||
|
ApiError{Message: "invalid base64 content", Code: http.StatusUnprocessableEntity}.Send(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var notFound []string
|
||||||
|
database.GetDB().Select("* FROM ? EXCEPT ?", payload.Authors, database.GetDB().Table("author").Select("id")).Find(¬Found)
|
||||||
|
if len(notFound) > 0 {
|
||||||
|
ApiError{fmt.Sprintf("no authors with the id(s) %s were found", strings.Join(notFound, ", ")), http.StatusUnprocessableEntity}.Send(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
a := article{
|
||||||
|
Title: payload.Title,
|
||||||
|
Summary: payload.Summary,
|
||||||
|
Image: payload.Image,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
Modified: time.Unix(0, 0).Unix(),
|
||||||
|
Link: "/" + path.Join(config.Prefix, "article", payload.Link),
|
||||||
|
Markdown: string(rawMarkdown),
|
||||||
|
Html: string(markdown.ToHTML(rawMarkdown, nil, nil)),
|
||||||
|
}
|
||||||
|
database.GetDB().Table("article").Create(&a)
|
||||||
|
var authors []map[string]interface{}
|
||||||
|
for _, author := range append([]int{authorId}, payload.Authors...) {
|
||||||
|
authors = append(authors, map[string]interface{}{
|
||||||
|
"article_id": a.ID,
|
||||||
|
"author_id": author,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
database.GetDB().Table("article_author").Create(&authors)
|
||||||
|
var tags []map[string]interface{}
|
||||||
|
for _, tag := range payload.Tags {
|
||||||
|
authors = append(authors, map[string]interface{}{
|
||||||
|
"article_id": a.ID,
|
||||||
|
"tag": tag,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
database.GetDB().Table("article_tag").Create(&tags)
|
||||||
|
|
||||||
|
var articleSummary schema.ArticleSummary
|
||||||
|
database.GetDB().Table("article").Find(&articleSummary, &a.ID)
|
||||||
|
database.GetDB().Table("author").Where("id IN (?)", database.GetDB().Table("article_author").Select("author_id").Where("article_id = ?", a.ID)).Find(&articleSummary.Authors)
|
||||||
|
if payload.Tags != nil {
|
||||||
|
articleSummary.Tags = payload.Tags
|
||||||
|
} else {
|
||||||
|
articleSummary.Tags = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
json.NewEncoder(w).Encode(articleSummary)
|
||||||
|
}
|
||||||
|
|
||||||
type editPayload struct {
|
type editPayload struct {
|
||||||
Id int `json:"id"`
|
Id int `json:"id"`
|
||||||
Title *string `json:"title"`
|
Title *string `json:"title"`
|
||||||
@@ -26,7 +115,7 @@ type editPayload struct {
|
|||||||
Content *string `json:"content"`
|
Content *string `json:"content"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func Edit(w http.ResponseWriter, r *http.Request) {
|
func articlePatch(w http.ResponseWriter, r *http.Request) {
|
||||||
authorId, ok := authorizedSession(r)
|
authorId, ok := authorizedSession(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
@@ -127,3 +216,32 @@ func Edit(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
json.NewEncoder(w).Encode(articleSummary)
|
json.NewEncoder(w).Encode(articleSummary)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type deletePayload struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func articleDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
|
authorId, ok := authorizedSession(r)
|
||||||
|
if !ok {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var payload deletePayload
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||||
|
InvalidJson.Send(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !database.Exists(database.GetDB().Table("article"), "id = ?", payload.Id) {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
} else if !database.Exists(database.GetDB().Table("article_author"), "article_id = ? AND author_id = ?", payload.Id, authorId) {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
database.GetDB().Table("article").Delete(&payload)
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
254
api/article_test.go
Normal file
254
api/article_test.go
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"TheAdversary/database"
|
||||||
|
"TheAdversary/schema"
|
||||||
|
"encoding/base64"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestArticlePost(t *testing.T) {
|
||||||
|
if err := initTestDatabase("upload_post_test.sqlite3"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
database.GetDB().Table("article").Create([]map[string]interface{}{
|
||||||
|
{
|
||||||
|
"title": "Upload test",
|
||||||
|
"summary": "An example article to test the upload api endpoint",
|
||||||
|
"created": time.Now().Unix(),
|
||||||
|
"link": "/article/upload-test",
|
||||||
|
"markdown": "Oh god i have to test all this, what am i doing with my life",
|
||||||
|
"html": "<p>Oh god i have to test all this, what am i doing with my life<p>",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
database.GetDB().Table("author").Create([]map[string]interface{}{
|
||||||
|
{
|
||||||
|
"name": "me",
|
||||||
|
"password": "",
|
||||||
|
"information": "this is my account",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
database.GetDB().Table("article_author").Create([]map[string]interface{}{
|
||||||
|
{
|
||||||
|
"article_id": 1,
|
||||||
|
"author_id": 1,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(articlePost))
|
||||||
|
checkTestInformation(t, server.URL, []testInformation{
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Code: http.StatusUnauthorized,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Cookie: map[string]string{
|
||||||
|
"session_id": initSession(),
|
||||||
|
},
|
||||||
|
Body: uploadPayload{
|
||||||
|
Title: "Testooo",
|
||||||
|
Summary: "I have no idea what to put in here",
|
||||||
|
Authors: []int{1},
|
||||||
|
Link: "testooo",
|
||||||
|
Content: base64.StdEncoding.EncodeToString([]byte("### Testo")),
|
||||||
|
},
|
||||||
|
ResultBody: schema.ArticleSummary{
|
||||||
|
Id: 2,
|
||||||
|
Title: "Testooo",
|
||||||
|
Summary: "I have no idea what to put in here",
|
||||||
|
Authors: []schema.Author{
|
||||||
|
{
|
||||||
|
Id: 1,
|
||||||
|
Name: "me",
|
||||||
|
Information: "this is my account",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Tags: []string{},
|
||||||
|
Link: "/article/testooo",
|
||||||
|
},
|
||||||
|
Code: http.StatusCreated,
|
||||||
|
AfterExec: func(information *testInformation) {
|
||||||
|
var created int64
|
||||||
|
database.GetDB().Table("article").Select("created").Where("id = 2").Find(&created)
|
||||||
|
res := information.ResultBody.(schema.ArticleSummary)
|
||||||
|
res.Created = created
|
||||||
|
information.ResultBody = res
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestArticlePatch(t *testing.T) {
|
||||||
|
if err := initTestDatabase("edit_test.sqlite3"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
database.GetDB().Table("article").Create([]map[string]interface{}{
|
||||||
|
{
|
||||||
|
"title": "test article",
|
||||||
|
"summary": "example summary",
|
||||||
|
"created": time.Now().Unix(),
|
||||||
|
"link": "/article/test-article",
|
||||||
|
"markdown": "Just a simple test article",
|
||||||
|
"html": "<p>Just a simple test article<p>",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
database.GetDB().Table("author").Create([]map[string]interface{}{
|
||||||
|
{
|
||||||
|
"name": "test",
|
||||||
|
"password": "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "admin",
|
||||||
|
"password": "123456",
|
||||||
|
"information": "im the admin",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
database.GetDB().Table("article_author").Create([]map[string]interface{}{
|
||||||
|
{
|
||||||
|
"article_id": 1,
|
||||||
|
"author_id": 1,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(articlePatch))
|
||||||
|
newTitle := "New title"
|
||||||
|
var created int64
|
||||||
|
database.GetDB().Table("article").Select("created").Where("id = 1").Find(&created)
|
||||||
|
checkTestInformation(t, server.URL, []testInformation{
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Code: http.StatusUnauthorized,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Cookie: map[string]string{
|
||||||
|
"session_id": initSession(),
|
||||||
|
},
|
||||||
|
Body: editPayload{
|
||||||
|
Id: 69,
|
||||||
|
},
|
||||||
|
Code: http.StatusNotFound,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Cookie: map[string]string{
|
||||||
|
"session_id": initSession(),
|
||||||
|
},
|
||||||
|
Body: editPayload{
|
||||||
|
Id: 1,
|
||||||
|
Title: &newTitle,
|
||||||
|
Authors: &[]int{
|
||||||
|
2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ResultBody: schema.ArticleSummary{
|
||||||
|
Id: 1,
|
||||||
|
Title: "New title",
|
||||||
|
Summary: "example summary",
|
||||||
|
Authors: []schema.Author{
|
||||||
|
{
|
||||||
|
Id: 1,
|
||||||
|
Name: "test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Id: 2,
|
||||||
|
Name: "admin",
|
||||||
|
Information: "im the admin",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Created: created,
|
||||||
|
Tags: []string{},
|
||||||
|
Link: "/article/test-article",
|
||||||
|
},
|
||||||
|
AfterExec: func(information *testInformation) {
|
||||||
|
var modified int64
|
||||||
|
database.GetDB().Table("article").Select("modified").Where("id = 1").Find(&modified)
|
||||||
|
res := information.ResultBody.(schema.ArticleSummary)
|
||||||
|
res.Modified = modified
|
||||||
|
information.ResultBody = res
|
||||||
|
},
|
||||||
|
Code: http.StatusOK,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestArticleDelete(t *testing.T) {
|
||||||
|
if err := initTestDatabase("delete_test.sqlite3"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
database.GetDB().Table("article").Create([]map[string]interface{}{
|
||||||
|
{
|
||||||
|
"title": "test",
|
||||||
|
"summary": "test summary",
|
||||||
|
"image": "https://upload.wikimedia.org/wikipedia/commons/0/05/Go_Logo_Blue.svg",
|
||||||
|
"created": time.Now().Unix(),
|
||||||
|
"modified": time.Now().Unix(),
|
||||||
|
"link": "/article/test",
|
||||||
|
"markdown": "# Title",
|
||||||
|
"html": "<h1>Title</h1>",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "owo",
|
||||||
|
"created": time.Now().Unix(),
|
||||||
|
"link": "/article/owo",
|
||||||
|
"markdown": "owo",
|
||||||
|
"html": "<p>owo<p>",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
database.GetDB().Table("author").Create(map[string]interface{}{
|
||||||
|
"name": "test",
|
||||||
|
"password": "",
|
||||||
|
})
|
||||||
|
database.GetDB().Table("article_author").Create([]map[string]interface{}{
|
||||||
|
{
|
||||||
|
"article_id": 1,
|
||||||
|
"author_id": 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"article_id": 2,
|
||||||
|
"author_id": 1,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(articleDelete))
|
||||||
|
checkTestInformation(t, server.URL, []testInformation{
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Code: http.StatusUnauthorized,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Body: deletePayload{
|
||||||
|
Id: 1,
|
||||||
|
},
|
||||||
|
Code: http.StatusUnauthorized,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Body: deletePayload{
|
||||||
|
Id: 1,
|
||||||
|
},
|
||||||
|
Cookie: map[string]string{
|
||||||
|
"session_id": initSession(),
|
||||||
|
},
|
||||||
|
Code: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Body: deletePayload{
|
||||||
|
Id: 69,
|
||||||
|
},
|
||||||
|
Cookie: map[string]string{
|
||||||
|
"session_id": initSession(),
|
||||||
|
},
|
||||||
|
Code: http.StatusNotFound,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"TheAdversary/database"
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type deletePayload struct {
|
|
||||||
Id int `json:"id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func Delete(w http.ResponseWriter, r *http.Request) {
|
|
||||||
authorId, ok := authorizedSession(r)
|
|
||||||
if !ok {
|
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var payload deletePayload
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
|
||||||
InvalidJson.Send(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !database.Exists(database.GetDB().Table("article"), "id = ?", payload.Id) {
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
return
|
|
||||||
} else if !database.Exists(database.GetDB().Table("article_author"), "article_id = ? AND author_id = ?", payload.Id, authorId) {
|
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
database.GetDB().Table("article").Delete(&payload)
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"TheAdversary/database"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDelete(t *testing.T) {
|
|
||||||
if err := initTestDatabase("delete_test.sqlite3"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
database.GetDB().Table("article").Create([]map[string]interface{}{
|
|
||||||
{
|
|
||||||
"title": "test",
|
|
||||||
"summary": "test summary",
|
|
||||||
"image": "https://upload.wikimedia.org/wikipedia/commons/0/05/Go_Logo_Blue.svg",
|
|
||||||
"created": time.Now().Unix(),
|
|
||||||
"modified": time.Now().Unix(),
|
|
||||||
"link": "/article/test",
|
|
||||||
"markdown": "# Title",
|
|
||||||
"html": "<h1>Title</h1>",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "owo",
|
|
||||||
"created": time.Now().Unix(),
|
|
||||||
"link": "/article/owo",
|
|
||||||
"markdown": "owo",
|
|
||||||
"html": "<p>owo<p>",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
database.GetDB().Table("author").Create(map[string]interface{}{
|
|
||||||
"name": "test",
|
|
||||||
"password": "",
|
|
||||||
})
|
|
||||||
database.GetDB().Table("article_author").Create([]map[string]interface{}{
|
|
||||||
{
|
|
||||||
"article_id": 1,
|
|
||||||
"author_id": 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"article_id": 2,
|
|
||||||
"author_id": 1,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
server := httptest.NewServer(http.HandlerFunc(Delete))
|
|
||||||
checkTestInformation(t, server.URL, []testInformation{
|
|
||||||
{
|
|
||||||
Method: http.MethodPost,
|
|
||||||
Code: http.StatusUnauthorized,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Method: http.MethodPost,
|
|
||||||
Body: deletePayload{
|
|
||||||
Id: 1,
|
|
||||||
},
|
|
||||||
Code: http.StatusUnauthorized,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Method: http.MethodPost,
|
|
||||||
Body: deletePayload{
|
|
||||||
Id: 1,
|
|
||||||
},
|
|
||||||
Cookie: map[string]string{
|
|
||||||
"session_id": initSession(),
|
|
||||||
},
|
|
||||||
Code: http.StatusOK,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Method: http.MethodPost,
|
|
||||||
Body: deletePayload{
|
|
||||||
Id: 69,
|
|
||||||
},
|
|
||||||
Cookie: map[string]string{
|
|
||||||
"session_id": initSession(),
|
|
||||||
},
|
|
||||||
Code: http.StatusNotFound,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
105
api/edit_test.go
105
api/edit_test.go
@@ -1,105 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"TheAdversary/database"
|
|
||||||
"TheAdversary/schema"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEdit(t *testing.T) {
|
|
||||||
if err := initTestDatabase("edit_test.sqlite3"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
database.GetDB().Table("article").Create([]map[string]interface{}{
|
|
||||||
{
|
|
||||||
"title": "test article",
|
|
||||||
"summary": "example summary",
|
|
||||||
"created": time.Now().Unix(),
|
|
||||||
"link": "/article/test-article",
|
|
||||||
"markdown": "Just a simple test article",
|
|
||||||
"html": "<p>Just a simple test article<p>",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
database.GetDB().Table("author").Create([]map[string]interface{}{
|
|
||||||
{
|
|
||||||
"name": "test",
|
|
||||||
"password": "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "admin",
|
|
||||||
"password": "123456",
|
|
||||||
"information": "im the admin",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
database.GetDB().Table("article_author").Create([]map[string]interface{}{
|
|
||||||
{
|
|
||||||
"article_id": 1,
|
|
||||||
"author_id": 1,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
server := httptest.NewServer(http.HandlerFunc(Edit))
|
|
||||||
newTitle := "New title"
|
|
||||||
var created int64
|
|
||||||
database.GetDB().Table("article").Select("created").Where("id = 1").Find(&created)
|
|
||||||
checkTestInformation(t, server.URL, []testInformation{
|
|
||||||
{
|
|
||||||
Method: http.MethodPost,
|
|
||||||
Code: http.StatusUnauthorized,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Method: http.MethodPost,
|
|
||||||
Cookie: map[string]string{
|
|
||||||
"session_id": initSession(),
|
|
||||||
},
|
|
||||||
Body: editPayload{
|
|
||||||
Id: 69,
|
|
||||||
},
|
|
||||||
Code: http.StatusNotFound,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Method: http.MethodPost,
|
|
||||||
Cookie: map[string]string{
|
|
||||||
"session_id": initSession(),
|
|
||||||
},
|
|
||||||
Body: editPayload{
|
|
||||||
Id: 1,
|
|
||||||
Title: &newTitle,
|
|
||||||
Authors: &[]int{
|
|
||||||
2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ResultBody: schema.ArticleSummary{
|
|
||||||
Id: 1,
|
|
||||||
Title: "New title",
|
|
||||||
Summary: "example summary",
|
|
||||||
Authors: []schema.Author{
|
|
||||||
{
|
|
||||||
Id: 1,
|
|
||||||
Name: "test",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Id: 2,
|
|
||||||
Name: "admin",
|
|
||||||
Information: "im the admin",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Created: created,
|
|
||||||
Tags: []string{},
|
|
||||||
Link: "/article/test-article",
|
|
||||||
},
|
|
||||||
AfterExec: func(information *testInformation) {
|
|
||||||
var modified int64
|
|
||||||
database.GetDB().Table("article").Select("modified").Where("id = 1").Find(&modified)
|
|
||||||
res := information.ResultBody.(schema.ArticleSummary)
|
|
||||||
res.Modified = modified
|
|
||||||
information.ResultBody = res
|
|
||||||
},
|
|
||||||
Code: http.StatusOK,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"TheAdversary/config"
|
|
||||||
"TheAdversary/database"
|
|
||||||
"TheAdversary/schema"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/gomarkdown/markdown"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
"net/http"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type uploadPayload struct {
|
|
||||||
Title string `json:"title"`
|
|
||||||
Summary string `json:"summary"`
|
|
||||||
Authors []int `json:"authors"`
|
|
||||||
Image string `json:"image"`
|
|
||||||
Tags []string `json:"tags"`
|
|
||||||
Link string `json:"link"`
|
|
||||||
Content string `json:"content"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func Upload(w http.ResponseWriter, r *http.Request) {
|
|
||||||
authorId, ok := authorizedSession(r)
|
|
||||||
if !ok {
|
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var payload uploadPayload
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
|
||||||
InvalidJson.Send(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rawMarkdown, err := base64.StdEncoding.DecodeString(payload.Content)
|
|
||||||
if err != nil {
|
|
||||||
zap.S().Warnf("Cannot decode base64")
|
|
||||||
ApiError{Message: "invalid base64 content", Code: http.StatusUnprocessableEntity}.Send(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var notFound []string
|
|
||||||
database.GetDB().Select("* FROM ? EXCEPT ?", payload.Authors, database.GetDB().Table("author").Select("id")).Find(¬Found)
|
|
||||||
if len(notFound) > 0 {
|
|
||||||
ApiError{fmt.Sprintf("no authors with the id(s) %s were found", strings.Join(notFound, ", ")), http.StatusUnprocessableEntity}.Send(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
a := article{
|
|
||||||
Title: payload.Title,
|
|
||||||
Summary: payload.Summary,
|
|
||||||
Image: payload.Image,
|
|
||||||
Created: time.Now().Unix(),
|
|
||||||
Modified: time.Unix(0, 0).Unix(),
|
|
||||||
Link: "/" + path.Join(config.Prefix, "article", payload.Link),
|
|
||||||
Markdown: string(rawMarkdown),
|
|
||||||
Html: string(markdown.ToHTML(rawMarkdown, nil, nil)),
|
|
||||||
}
|
|
||||||
database.GetDB().Table("article").Create(&a)
|
|
||||||
var authors []map[string]interface{}
|
|
||||||
for _, author := range append([]int{authorId}, payload.Authors...) {
|
|
||||||
authors = append(authors, map[string]interface{}{
|
|
||||||
"article_id": a.ID,
|
|
||||||
"author_id": author,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
database.GetDB().Table("article_author").Create(&authors)
|
|
||||||
var tags []map[string]interface{}
|
|
||||||
for _, tag := range payload.Tags {
|
|
||||||
authors = append(authors, map[string]interface{}{
|
|
||||||
"article_id": a.ID,
|
|
||||||
"tag": tag,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
database.GetDB().Table("article_tag").Create(&tags)
|
|
||||||
|
|
||||||
var articleSummary schema.ArticleSummary
|
|
||||||
database.GetDB().Table("article").Find(&articleSummary, &a.ID)
|
|
||||||
database.GetDB().Table("author").Where("id IN (?)", database.GetDB().Table("article_author").Select("author_id").Where("article_id = ?", a.ID)).Find(&articleSummary.Authors)
|
|
||||||
if payload.Tags != nil {
|
|
||||||
articleSummary.Tags = payload.Tags
|
|
||||||
} else {
|
|
||||||
articleSummary.Tags = []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusCreated)
|
|
||||||
json.NewEncoder(w).Encode(articleSummary)
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"TheAdversary/database"
|
|
||||||
"TheAdversary/schema"
|
|
||||||
"encoding/base64"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestUpload(t *testing.T) {
|
|
||||||
if err := initTestDatabase("upload_test.sqlite3"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
database.GetDB().Table("article").Create([]map[string]interface{}{
|
|
||||||
{
|
|
||||||
"title": "Upload test",
|
|
||||||
"summary": "An example article to test the upload api endpoint",
|
|
||||||
"created": time.Now().Unix(),
|
|
||||||
"link": "/article/upload-test",
|
|
||||||
"markdown": "Oh god i have to test all this, what am i doing with my life",
|
|
||||||
"html": "<p>Oh god i have to test all this, what am i doing with my life<p>",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
database.GetDB().Table("author").Create([]map[string]interface{}{
|
|
||||||
{
|
|
||||||
"name": "me",
|
|
||||||
"password": "",
|
|
||||||
"information": "this is my account",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
database.GetDB().Table("article_author").Create([]map[string]interface{}{
|
|
||||||
{
|
|
||||||
"article_id": 1,
|
|
||||||
"author_id": 1,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
server := httptest.NewServer(http.HandlerFunc(Upload))
|
|
||||||
checkTestInformation(t, server.URL, []testInformation{
|
|
||||||
{
|
|
||||||
Method: http.MethodPost,
|
|
||||||
Code: http.StatusUnauthorized,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Method: http.MethodPost,
|
|
||||||
Cookie: map[string]string{
|
|
||||||
"session_id": initSession(),
|
|
||||||
},
|
|
||||||
Body: uploadPayload{
|
|
||||||
Title: "Testooo",
|
|
||||||
Summary: "I have no idea what to put in here",
|
|
||||||
Authors: []int{1},
|
|
||||||
Link: "testooo",
|
|
||||||
Content: base64.StdEncoding.EncodeToString([]byte("### Testo")),
|
|
||||||
},
|
|
||||||
ResultBody: schema.ArticleSummary{
|
|
||||||
Id: 2,
|
|
||||||
Title: "Testooo",
|
|
||||||
Summary: "I have no idea what to put in here",
|
|
||||||
Authors: []schema.Author{
|
|
||||||
{
|
|
||||||
Id: 1,
|
|
||||||
Name: "me",
|
|
||||||
Information: "this is my account",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Tags: []string{},
|
|
||||||
Link: "/article/testooo",
|
|
||||||
},
|
|
||||||
Code: http.StatusCreated,
|
|
||||||
AfterExec: func(information *testInformation) {
|
|
||||||
var created int64
|
|
||||||
database.GetDB().Table("article").Select("created").Where("id = 2").Find(&created)
|
|
||||||
res := information.ResultBody.(schema.ArticleSummary)
|
|
||||||
res.Created = created
|
|
||||||
information.ResultBody = res
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
4
main.go
4
main.go
@@ -23,9 +23,7 @@ func main() {
|
|||||||
r.HandleFunc("/api/recent", api.Recent).Methods(http.MethodGet)
|
r.HandleFunc("/api/recent", api.Recent).Methods(http.MethodGet)
|
||||||
r.HandleFunc("/api/search", api.Search).Methods(http.MethodGet)
|
r.HandleFunc("/api/search", api.Search).Methods(http.MethodGet)
|
||||||
|
|
||||||
r.HandleFunc("/api/upload", api.Upload).Methods(http.MethodPost)
|
r.HandleFunc("/api/article", api.Article).Methods(http.MethodPost, http.MethodPatch, http.MethodDelete)
|
||||||
r.HandleFunc("/api/edit", api.Edit).Methods(http.MethodPost)
|
|
||||||
r.HandleFunc("/api/delete", api.Delete).Methods(http.MethodPost)
|
|
||||||
|
|
||||||
r.HandleFunc("/api/assets", api.Assets).Methods(http.MethodGet, http.MethodPost, http.MethodDelete)
|
r.HandleFunc("/api/assets", api.Assets).Methods(http.MethodGet, http.MethodPost, http.MethodDelete)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user