Skip to content

Commit

Permalink
Finally done
Browse files Browse the repository at this point in the history
  • Loading branch information
petuhovskiy committed Jan 19, 2019
1 parent eb0fc78 commit 2ef9039
Show file tree
Hide file tree
Showing 41 changed files with 499 additions and 628 deletions.
2 changes: 1 addition & 1 deletion model/article.go → article/article.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package model
package article

import (
"fmt"
Expand Down
2 changes: 1 addition & 1 deletion model/article_status.go → article/article_status.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package model
package article

import "time"

Expand Down
2 changes: 1 addition & 1 deletion model/article_version.go → article/article_version.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package model
package article

import (
"fmt"
Expand Down
92 changes: 92 additions & 0 deletions article/controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package article

import (
"github.com/go-chi/render"
"github.com/rwlist/rwcore/resp"
"net/http"
"strconv"
)

type Controller struct {
Service Service
}

func NewController(s Service) *Controller {
return &Controller{
Service: s,
}
}

func (c *Controller) get(w http.ResponseWriter, r *http.Request) {
article := R(r)
render.Respond(w, r, article)
}

func (c *Controller) addURL(w http.ResponseWriter, r *http.Request) {
db := DB(r)

var url string
err := render.Decode(r, &url)
if err != nil {
render.Render(w, r, resp.ErrBadRequest.With(err))
return
}
res, err := c.Service.AddURL(db, url)
resp.QuickRespond(w, r, res, err)
}

func (c *Controller) getAll(w http.ResponseWriter, r *http.Request) {
db := DB(r)

all, err := db.GetAll()
resp.QuickRespond(w, r, all, err)
}

func (c *Controller) onClick(w http.ResponseWriter, r *http.Request) {
db := DB(r)
article := R(r)

res, err := c.Service.OnClick(db, article)
resp.QuickRespond(w, r, res, err)
}

func (c *Controller) setReadStatus(w http.ResponseWriter, r *http.Request) {
db := DB(r)
article := R(r)

newStatus := r.URL.Query().Get("newStatus")
res, err := c.Service.SetReadStatus(db, article, newStatus)
resp.QuickRespond(w, r, res, err)
}

func (c *Controller) changeRating(w http.ResponseWriter, r *http.Request) {
db := DB(r)
article := R(r)

delta, err := strconv.Atoi(r.URL.Query().Get("delta"))
if err != nil {
render.Render(w, r, resp.ErrBadRequest.With(err))
return
}

res, err := c.Service.ChangeRating(db, article, delta)
resp.QuickRespond(w, r, res, err)
}

func (c *Controller) removeTag(w http.ResponseWriter, r *http.Request) {
db := DB(r)
article := R(r)
tag := r.URL.Query().Get("tag")

res, err := c.Service.RemoveTag(db, article, tag)
resp.QuickRespond(w, r, res, err)
}

func (c *Controller) addTag(w http.ResponseWriter, r *http.Request) {
db := DB(r)
article := R(r)
tag := r.URL.Query().Get("tag")

res, err := c.Service.AddTag(db, article, tag)
resp.QuickRespond(w, r, res, err)
}
23 changes: 23 additions & 0 deletions article/entity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package article

import (
"github.com/globalsign/mgo/bson"
"github.com/google/wire"
)

type ArticleUpdate struct {
ArticleID bson.ObjectId `json:"id"`
Article Article `json:"article"`
}

type ArticlesActionResp struct {
AddedArticles []Article `json:"addedArticles"`
Errors []error `json:"errors"`
}

var All = wire.NewSet( // TODO
NewController,
NewMiddleware,
NewRouter,
NewService,
)
23 changes: 11 additions & 12 deletions articles/middleware.go → article/middleware.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
package articles
package article

import (
"context"
"errors"
"github.com/rwlist/rwcore/cxt"
"github.com/rwlist/rwcore/resp"
"net/http"

"github.com/globalsign/mgo/bson"
"github.com/go-chi/chi"
"github.com/go-chi/render"
"github.com/rwlist/rwcore/app/db"
"github.com/rwlist/rwcore/app/model"
"github.com/rwlist/rwcore/app/resp"
)

type ctxkey string
type Middleware struct{}

var (
articleKey ctxkey = "article"
)
func NewMiddleware() *Middleware {
return &Middleware{}
}

func fetchArticle(next http.Handler) http.Handler {
func (m *Middleware) UpdateContext(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
articles := db.From(r).Articles()
articles := DB(r)

id := chi.URLParam(r, "id")
if !bson.IsObjectIdHex(id) {
Expand All @@ -31,14 +30,14 @@ func fetchArticle(next http.Handler) http.Handler {
}
bid := bson.ObjectIdHex(id)

var article model.Article
var article Article
err := articles.FindId(bid).One(&article)
if err != nil {
render.Render(w, r, resp.ErrNotFound.With(err))
return
}

ctx = context.WithValue(ctx, articleKey, article)
ctx = context.WithValue(ctx, cxt.ArticleKey, article)
next.ServeHTTP(w, r.WithContext(ctx))
}
return http.HandlerFunc(fn)
Expand Down
29 changes: 29 additions & 0 deletions article/router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package article

import (
"github.com/go-chi/chi"
"github.com/rwlist/rwcore/auth"
)

type Router struct{ *chi.Mux }

func NewRouter(c *Controller, auth *auth.Middleware, mid *Middleware) Router {
r := chi.NewRouter()
r.Use(auth.HasRole("article"))

// r.Post("/add", m.addOne)
// r.Post("/addMany", m.addMany)
r.Get("/all", c.getAll)
r.Post("/addURL", c.addURL)
r.Route("/{id}", func(r chi.Router) {
r.Use(mid.UpdateContext)
r.Get("/", c.get)
r.Post("/click", c.onClick)
r.Post("/read/status", c.setReadStatus)
r.Post("/rating/change", c.changeRating)
r.Post("/tags/remove", c.removeTag)
r.Post("/tags/add", c.addTag)
// r.Patch("/patch", m.patch)
})
return Router{r}
}
102 changes: 102 additions & 0 deletions article/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package article

import (
"errors"
"time"

"github.com/rwlist/rwcore/utils"
)

// TODO: queue/mutex
type Service struct{}

func NewService() Service {
return Service{}
}

func (s Service) AddURL(db Store, url string) (*ArticlesActionResp, error) {
resp := &ArticlesActionResp{}

title, err := utils.ParseTitleByURL(url)
if err != nil {
resp.Errors = append(resp.Errors, err)
}

if title == "" {
title = "Unknown HTML Title"
}

article := NewArticle(url, title, map[string]interface{}{
"added": "url",
})

err = db.InsertOne(&article)
if err != nil {
return nil, err
}

resp.AddedArticles = append(resp.AddedArticles, article)

return resp, nil
}

func (s Service) OnClick(db Store, article Article) (*ArticleUpdate, error) {
now := time.Now()

article.Status.Clicks++
article.Status.LastClick = &now
if article.Status.ReadStatus == "unopened" {
article.Status.ReadStatus = "viewed"
article.Status.ReadStatusChange = &now
}

err := db.UpdateOne(&article)
return asUpdate(article), err
}

func (s Service) SetReadStatus(db Store, article Article, newStatus string) (*ArticleUpdate, error) {
now := time.Now()

if !ValidArticleReadStatus(newStatus) {
return nil, errors.New("invalid read status")
}

article.Status.ReadStatus = newStatus
article.Status.ReadStatusChange = &now

err := db.UpdateOne(&article)
return asUpdate(article), err
}

func (s Service) ChangeRating(db Store, article Article, delta int) (*ArticleUpdate, error) {
article.Status.Rating += delta

err := db.UpdateOne(&article)
return asUpdate(article), err
}

func (s Service) RemoveTag(db Store, article Article, tag string) (*ArticleUpdate, error) {
now := time.Now()

err := article.RemoveTag(tag)
if err != nil {
return nil, err
}
article.Modified = &now

err = db.UpdateOne(&article)
return asUpdate(article), err
}

func (s Service) AddTag(db Store, article Article, tag string) (*ArticleUpdate, error) {
now := time.Now()

err := article.AddTag(tag)
if err != nil {
return nil, err
}
article.Modified = &now

err = db.UpdateOne(&article)
return asUpdate(article), err
}
33 changes: 23 additions & 10 deletions store/articles.go → article/store.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
package store
package article

import (
"github.com/globalsign/mgo"
"github.com/globalsign/mgo/bson"
"github.com/rwlist/rwcore/app/model"
"github.com/rwlist/rwcore/cxt"
"net/http"
)

type Articles struct {
const (
CollName = "article"
)

func S(db *mgo.Database) Store {
return Store{db.C(CollName)}
}

func DB(r *http.Request) Store {
return S(cxt.DB(r.Context()))
}

type Store struct {
*mgo.Collection
}

func (s Articles) Init() error {
func (s Store) Init() error {
iter := s.Find(bson.M{}).Iter()
var article model.Article
var article Article
for iter.Next(&article) {
ok, err := article.BumpVersion()
if err != nil {
Expand All @@ -30,18 +43,18 @@ func (s Articles) Init() error {
return iter.Close()
}

func (s Articles) GetAll() ([]model.Article, error) {
var articles []model.Article
func (s Store) GetAll() ([]Article, error) {
var articles []Article
err := s.Find(bson.M{}).All(&articles)
return articles, err
}

func (s Articles) InsertOne(article *model.Article) (err error) {
func (s Store) InsertOne(article *Article) (err error) {
err = s.Insert(article)
return
}

func (s Articles) InsertMany(articles []model.Article) (err error) {
func (s Store) InsertMany(articles []Article) (err error) {
iSlice := make([]interface{}, len(articles))
for k, v := range articles {
iSlice[k] = v
Expand All @@ -50,6 +63,6 @@ func (s Articles) InsertMany(articles []model.Article) (err error) {
return
}

func (s Articles) UpdateOne(article *model.Article) (err error) {
func (s Store) UpdateOne(article *Article) (err error) {
return s.UpdateId(article.ID, article)
}
Loading

0 comments on commit 2ef9039

Please sign in to comment.