mirror of
https://github.com/usememos/memos.git
synced 2025-02-12 01:10:38 +01:00
refactor: raw struct for store
This commit is contained in:
parent
0b50122aac
commit
bc22f69ac5
21
api/api.go
Normal file
21
api/api.go
Normal file
@ -0,0 +1,21 @@
|
||||
package api
|
||||
|
||||
// RowStatus is the status for a row.
|
||||
type RowStatus string
|
||||
|
||||
const (
|
||||
// Normal is the status for a normal row.
|
||||
Normal RowStatus = "NORMAL"
|
||||
// Archived is the status for an archived row.
|
||||
Archived RowStatus = "ARCHIVED"
|
||||
)
|
||||
|
||||
func (e RowStatus) String() string {
|
||||
switch e {
|
||||
case Normal:
|
||||
return "NORMAL"
|
||||
case Archived:
|
||||
return "ARCHIVED"
|
||||
}
|
||||
return ""
|
||||
}
|
20
api/memo.go
20
api/memo.go
@ -4,13 +4,14 @@ type Memo struct {
|
||||
ID int `json:"id"`
|
||||
|
||||
// Standard fields
|
||||
CreatedTs int64 `json:"createdTs"`
|
||||
UpdatedTs int64 `json:"updatedTs"`
|
||||
RowStatus string `json:"rowStatus"`
|
||||
RowStatus RowStatus `json:"rowStatus"`
|
||||
CreatorID int `json:"creatorId"`
|
||||
CreatedTs int64 `json:"createdTs"`
|
||||
UpdatedTs int64 `json:"updatedTs"`
|
||||
|
||||
// Domain specific fields
|
||||
Content string `json:"content"`
|
||||
CreatorID int `json:"creatorId"`
|
||||
Content string `json:"content"`
|
||||
Pinned bool `json:"pinned"`
|
||||
}
|
||||
|
||||
type MemoCreate struct {
|
||||
@ -27,7 +28,7 @@ type MemoPatch struct {
|
||||
ID int
|
||||
|
||||
// Standard fields
|
||||
RowStatus *string `json:"rowStatus"`
|
||||
RowStatus *RowStatus `json:"rowStatus"`
|
||||
|
||||
// Domain specific fields
|
||||
Content *string `json:"content"`
|
||||
@ -37,8 +38,11 @@ type MemoFind struct {
|
||||
ID *int `json:"id"`
|
||||
|
||||
// Standard fields
|
||||
CreatorID *int `json:"creatorId"`
|
||||
RowStatus *string `json:"rowStatus"`
|
||||
RowStatus *RowStatus `json:"rowStatus"`
|
||||
CreatorID *int `json:"creatorId"`
|
||||
|
||||
// Domain specific fields
|
||||
Pinned *bool
|
||||
}
|
||||
|
||||
type MemoDelete struct {
|
||||
|
21
api/memo_organizer.go
Normal file
21
api/memo_organizer.go
Normal file
@ -0,0 +1,21 @@
|
||||
package api
|
||||
|
||||
type MemoOrganizer struct {
|
||||
ID int
|
||||
|
||||
// Domain specific fields
|
||||
MemoID int
|
||||
UserID int
|
||||
Pinned bool
|
||||
}
|
||||
|
||||
type MemoOrganizerFind struct {
|
||||
MemoID int
|
||||
UserID int
|
||||
}
|
||||
|
||||
type MemoOrganizerUpsert struct {
|
||||
MemoID int
|
||||
UserID int
|
||||
Pinned bool `json:"pinned"`
|
||||
}
|
@ -4,10 +4,10 @@ type Shortcut struct {
|
||||
ID int `json:"id"`
|
||||
|
||||
// Standard fields
|
||||
CreatorID int
|
||||
CreatedTs int64 `json:"createdTs"`
|
||||
UpdatedTs int64 `json:"updatedTs"`
|
||||
RowStatus string `json:"rowStatus"`
|
||||
RowStatus RowStatus `json:"rowStatus"`
|
||||
CreatorID int `json:"creatorId"`
|
||||
CreatedTs int64 `json:"createdTs"`
|
||||
UpdatedTs int64 `json:"updatedTs"`
|
||||
|
||||
// Domain specific fields
|
||||
Title string `json:"title"`
|
||||
@ -27,7 +27,7 @@ type ShortcutPatch struct {
|
||||
ID int
|
||||
|
||||
// Standard fields
|
||||
RowStatus *string `json:"rowStatus"`
|
||||
RowStatus *RowStatus `json:"rowStatus"`
|
||||
|
||||
// Domain specific fields
|
||||
Title *string `json:"title"`
|
||||
|
21
api/user.go
21
api/user.go
@ -10,12 +10,23 @@ const (
|
||||
NormalUser Role = "USER"
|
||||
)
|
||||
|
||||
func (e Role) String() string {
|
||||
switch e {
|
||||
case Owner:
|
||||
return "OWNER"
|
||||
case NormalUser:
|
||||
return "USER"
|
||||
}
|
||||
return "USER"
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID int `json:"id"`
|
||||
|
||||
// Standard fields
|
||||
CreatedTs int64 `json:"createdTs"`
|
||||
UpdatedTs int64 `json:"updatedTs"`
|
||||
RowStatus RowStatus `json:"rowStatus"`
|
||||
CreatedTs int64 `json:"createdTs"`
|
||||
UpdatedTs int64 `json:"updatedTs"`
|
||||
|
||||
// Domain specific fields
|
||||
Email string `json:"email"`
|
||||
@ -38,6 +49,9 @@ type UserCreate struct {
|
||||
type UserPatch struct {
|
||||
ID int
|
||||
|
||||
// Standard fields
|
||||
RowStatus *RowStatus `json:"rowStatus"`
|
||||
|
||||
// Domain specific fields
|
||||
Email *string `json:"email"`
|
||||
Name *string `json:"name"`
|
||||
@ -50,6 +64,9 @@ type UserPatch struct {
|
||||
type UserFind struct {
|
||||
ID *int `json:"id"`
|
||||
|
||||
// Standard fields
|
||||
RowStatus *RowStatus `json:"rowStatus"`
|
||||
|
||||
// Domain specific fields
|
||||
Email *string `json:"email"`
|
||||
Role *Role
|
||||
|
@ -27,6 +27,8 @@ func (s *Server) registerAuthRoutes(g *echo.Group) {
|
||||
}
|
||||
if user == nil {
|
||||
return echo.NewHTTPError(http.StatusUnauthorized, fmt.Sprintf("User not found with email %s", login.Email))
|
||||
} else if user.RowStatus == api.Archived {
|
||||
return echo.NewHTTPError(http.StatusForbidden, fmt.Sprintf("User has been archived with email %s", login.Email))
|
||||
}
|
||||
|
||||
// Compare the stored hashed password, with the hashed version of the password that was received.
|
||||
|
@ -85,6 +85,8 @@ func BasicAuthMiddleware(s *Server, next echo.HandlerFunc) echo.HandlerFunc {
|
||||
}
|
||||
if user == nil {
|
||||
return echo.NewHTTPError(http.StatusUnauthorized, fmt.Sprintf("Not found user ID: %d", userID))
|
||||
} else if user.RowStatus == api.Archived {
|
||||
return echo.NewHTTPError(http.StatusForbidden, fmt.Sprintf("User has been archived with email %s", user.Email))
|
||||
}
|
||||
|
||||
// Stores userID into context.
|
||||
|
@ -65,10 +65,16 @@ func (s *Server) registerMemoRoutes(g *echo.Group) {
|
||||
memoFind := &api.MemoFind{
|
||||
CreatorID: &userID,
|
||||
}
|
||||
rowStatus := c.QueryParam("rowStatus")
|
||||
|
||||
rowStatus := api.RowStatus(c.QueryParam("rowStatus"))
|
||||
if rowStatus != "" {
|
||||
memoFind.RowStatus = &rowStatus
|
||||
}
|
||||
pinnedStr := c.QueryParam("pinned")
|
||||
if pinnedStr != "" {
|
||||
pinned := pinnedStr == "true"
|
||||
memoFind.Pinned = &pinned
|
||||
}
|
||||
|
||||
list, err := s.Store.FindMemoList(memoFind)
|
||||
if err != nil {
|
||||
@ -83,6 +89,45 @@ func (s *Server) registerMemoRoutes(g *echo.Group) {
|
||||
return nil
|
||||
})
|
||||
|
||||
g.POST("/memo/:memoId/organizer", func(c echo.Context) error {
|
||||
memoID, err := strconv.Atoi(c.Param("memoId"))
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.Param("memoId"))).SetInternal(err)
|
||||
}
|
||||
|
||||
userID := c.Get(getUserIDContextKey()).(int)
|
||||
memoOrganizerUpsert := &api.MemoOrganizerUpsert{
|
||||
MemoID: memoID,
|
||||
UserID: userID,
|
||||
}
|
||||
if err := json.NewDecoder(c.Request().Body).Decode(memoOrganizerUpsert); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post memo organizer request").SetInternal(err)
|
||||
}
|
||||
|
||||
err = s.Store.UpsertMemoOrganizer(memoOrganizerUpsert)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to upsert memo organizer").SetInternal(err)
|
||||
}
|
||||
|
||||
memo, err := s.Store.FindMemo(&api.MemoFind{
|
||||
ID: &memoID,
|
||||
})
|
||||
if err != nil {
|
||||
if common.ErrorCode(err) == common.NotFound {
|
||||
return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("Memo ID not found: %d", memoID)).SetInternal(err)
|
||||
}
|
||||
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to find memo by ID: %v", memoID)).SetInternal(err)
|
||||
}
|
||||
|
||||
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8)
|
||||
if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(memo)); err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode memo response").SetInternal(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
g.GET("/memo/:memoId", func(c echo.Context) error {
|
||||
memoID, err := strconv.Atoi(c.Param("memoId"))
|
||||
if err != nil {
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"memos/api"
|
||||
"memos/common"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
@ -84,19 +85,6 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted patch user request").SetInternal(err)
|
||||
}
|
||||
|
||||
if userPatch.Email != nil {
|
||||
userFind := api.UserFind{
|
||||
Email: userPatch.Email,
|
||||
}
|
||||
user, err := s.Store.FindUser(&userFind)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to find user by email %s", *userPatch.Email)).SetInternal(err)
|
||||
}
|
||||
if user != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("User with email %s existed", *userPatch.Email)).SetInternal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if userPatch.Password != nil && *userPatch.Password != "" {
|
||||
passwordHash, err := bcrypt.GenerateFromPassword([]byte(*userPatch.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
@ -124,4 +112,53 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
g.PATCH("/user/:userId", func(c echo.Context) error {
|
||||
currentUserID := c.Get(getUserIDContextKey()).(int)
|
||||
currentUser, err := s.Store.FindUser(&api.UserFind{
|
||||
ID: ¤tUserID,
|
||||
})
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find user").SetInternal(err)
|
||||
}
|
||||
if currentUser == nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Current session user not found with ID: %d", currentUserID)).SetInternal(err)
|
||||
} else if currentUser.Role != api.Owner {
|
||||
return echo.NewHTTPError(http.StatusForbidden, "Access forbidden for current session user").SetInternal(err)
|
||||
}
|
||||
|
||||
userID, err := strconv.Atoi(c.Param("userId"))
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.Param("userId"))).SetInternal(err)
|
||||
}
|
||||
|
||||
userPatch := &api.UserPatch{
|
||||
ID: userID,
|
||||
}
|
||||
if err := json.NewDecoder(c.Request().Body).Decode(userPatch); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted patch user request").SetInternal(err)
|
||||
}
|
||||
|
||||
if userPatch.Password != nil && *userPatch.Password != "" {
|
||||
passwordHash, err := bcrypt.GenerateFromPassword([]byte(*userPatch.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to generate password hash").SetInternal(err)
|
||||
}
|
||||
|
||||
passwordHashStr := string(passwordHash)
|
||||
userPatch.PasswordHash = &passwordHashStr
|
||||
}
|
||||
|
||||
user, err := s.Store.PatchUser(userPatch)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to patch user").SetInternal(err)
|
||||
}
|
||||
|
||||
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8)
|
||||
if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(user)); err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode user response").SetInternal(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ func (s *Server) registerWebhookRoutes(g *echo.Group) {
|
||||
memoFind := &api.MemoFind{
|
||||
CreatorID: &user.ID,
|
||||
}
|
||||
rowStatus := c.QueryParam("rowStatus")
|
||||
rowStatus := api.RowStatus(c.QueryParam("rowStatus"))
|
||||
if rowStatus != "" {
|
||||
memoFind.RowStatus = &rowStatus
|
||||
}
|
||||
|
147
store/memo.go
147
store/memo.go
@ -7,8 +7,45 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// memoRaw is the store model for an Memo.
|
||||
// Fields have exactly the same meanings as Memo.
|
||||
type memoRaw struct {
|
||||
ID int
|
||||
|
||||
// Standard fields
|
||||
RowStatus api.RowStatus
|
||||
CreatorID int
|
||||
CreatedTs int64
|
||||
UpdatedTs int64
|
||||
|
||||
// Domain specific fields
|
||||
Content string
|
||||
}
|
||||
|
||||
// toMemo creates an instance of Memo based on the memoRaw.
|
||||
// This is intended to be called when we need to compose an Memo relationship.
|
||||
func (raw *memoRaw) toMemo() *api.Memo {
|
||||
return &api.Memo{
|
||||
ID: raw.ID,
|
||||
|
||||
// Standard fields
|
||||
RowStatus: raw.RowStatus,
|
||||
CreatorID: raw.CreatorID,
|
||||
CreatedTs: raw.CreatedTs,
|
||||
UpdatedTs: raw.UpdatedTs,
|
||||
|
||||
// Domain specific fields
|
||||
Content: raw.Content,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store) CreateMemo(create *api.MemoCreate) (*api.Memo, error) {
|
||||
memo, err := createMemo(s.db, create)
|
||||
memoRaw, err := createMemoRaw(s.db, create)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
memo, err := s.composeMemo(memoRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -17,7 +54,12 @@ func (s *Store) CreateMemo(create *api.MemoCreate) (*api.Memo, error) {
|
||||
}
|
||||
|
||||
func (s *Store) PatchMemo(patch *api.MemoPatch) (*api.Memo, error) {
|
||||
memo, err := patchMemo(s.db, patch)
|
||||
memoRaw, err := patchMemoRaw(s.db, patch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
memo, err := s.composeMemo(memoRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -26,16 +68,26 @@ func (s *Store) PatchMemo(patch *api.MemoPatch) (*api.Memo, error) {
|
||||
}
|
||||
|
||||
func (s *Store) FindMemoList(find *api.MemoFind) ([]*api.Memo, error) {
|
||||
list, err := findMemoList(s.db, find)
|
||||
memoRawList, err := findMemoRawList(s.db, find)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list := []*api.Memo{}
|
||||
for _, raw := range memoRawList {
|
||||
memo, err := s.composeMemo(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list = append(list, memo)
|
||||
}
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (s *Store) FindMemo(find *api.MemoFind) (*api.Memo, error) {
|
||||
list, err := findMemoList(s.db, find)
|
||||
list, err := findMemoRawList(s.db, find)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -44,7 +96,12 @@ func (s *Store) FindMemo(find *api.MemoFind) (*api.Memo, error) {
|
||||
return nil, &common.Error{Code: common.NotFound, Err: fmt.Errorf("not found")}
|
||||
}
|
||||
|
||||
return list[0], nil
|
||||
memo, err := s.composeMemo(list[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return memo, nil
|
||||
}
|
||||
|
||||
func (s *Store) DeleteMemo(delete *api.MemoDelete) error {
|
||||
@ -56,7 +113,7 @@ func (s *Store) DeleteMemo(delete *api.MemoDelete) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func createMemo(db *DB, create *api.MemoCreate) (*api.Memo, error) {
|
||||
func createMemoRaw(db *DB, create *api.MemoCreate) (*memoRaw, error) {
|
||||
set := []string{"creator_id", "content"}
|
||||
placeholder := []string{"?", "?"}
|
||||
args := []interface{}{create.CreatorID, create.Content}
|
||||
@ -79,26 +136,23 @@ func createMemo(db *DB, create *api.MemoCreate) (*api.Memo, error) {
|
||||
}
|
||||
defer row.Close()
|
||||
|
||||
if !row.Next() {
|
||||
return nil, &common.Error{Code: common.NotFound, Err: fmt.Errorf("not found")}
|
||||
}
|
||||
|
||||
var memo api.Memo
|
||||
row.Next()
|
||||
var memoRaw memoRaw
|
||||
if err := row.Scan(
|
||||
&memo.ID,
|
||||
&memo.CreatorID,
|
||||
&memo.CreatedTs,
|
||||
&memo.UpdatedTs,
|
||||
&memo.Content,
|
||||
&memo.RowStatus,
|
||||
&memoRaw.ID,
|
||||
&memoRaw.CreatorID,
|
||||
&memoRaw.CreatedTs,
|
||||
&memoRaw.UpdatedTs,
|
||||
&memoRaw.Content,
|
||||
&memoRaw.RowStatus,
|
||||
); err != nil {
|
||||
return nil, FormatError(err)
|
||||
}
|
||||
|
||||
return &memo, nil
|
||||
return &memoRaw, nil
|
||||
}
|
||||
|
||||
func patchMemo(db *DB, patch *api.MemoPatch) (*api.Memo, error) {
|
||||
func patchMemoRaw(db *DB, patch *api.MemoPatch) (*memoRaw, error) {
|
||||
set, args := []string{}, []interface{}{}
|
||||
|
||||
if v := patch.Content; v != nil {
|
||||
@ -125,21 +179,21 @@ func patchMemo(db *DB, patch *api.MemoPatch) (*api.Memo, error) {
|
||||
return nil, &common.Error{Code: common.NotFound, Err: fmt.Errorf("not found")}
|
||||
}
|
||||
|
||||
var memo api.Memo
|
||||
var memoRaw memoRaw
|
||||
if err := row.Scan(
|
||||
&memo.ID,
|
||||
&memo.CreatedTs,
|
||||
&memo.UpdatedTs,
|
||||
&memo.Content,
|
||||
&memo.RowStatus,
|
||||
&memoRaw.ID,
|
||||
&memoRaw.CreatedTs,
|
||||
&memoRaw.UpdatedTs,
|
||||
&memoRaw.Content,
|
||||
&memoRaw.RowStatus,
|
||||
); err != nil {
|
||||
return nil, FormatError(err)
|
||||
}
|
||||
|
||||
return &memo, nil
|
||||
return &memoRaw, nil
|
||||
}
|
||||
|
||||
func findMemoList(db *DB, find *api.MemoFind) ([]*api.Memo, error) {
|
||||
func findMemoRawList(db *DB, find *api.MemoFind) ([]*memoRaw, error) {
|
||||
where, args := []string{"1 = 1"}, []interface{}{}
|
||||
|
||||
if v := find.ID; v != nil {
|
||||
@ -151,6 +205,9 @@ func findMemoList(db *DB, find *api.MemoFind) ([]*api.Memo, error) {
|
||||
if v := find.RowStatus; v != nil {
|
||||
where, args = append(where, "row_status = ?"), append(args, *v)
|
||||
}
|
||||
if v := find.Pinned; v != nil {
|
||||
where = append(where, "id in (SELECT memo_id FROM memo_organizer WHERE pinned = 1 AND user_id = memo.creator_id )")
|
||||
}
|
||||
|
||||
rows, err := db.Db.Query(`
|
||||
SELECT
|
||||
@ -169,28 +226,28 @@ func findMemoList(db *DB, find *api.MemoFind) ([]*api.Memo, error) {
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
list := make([]*api.Memo, 0)
|
||||
memoRawList := make([]*memoRaw, 0)
|
||||
for rows.Next() {
|
||||
var memo api.Memo
|
||||
var memoRaw memoRaw
|
||||
if err := rows.Scan(
|
||||
&memo.ID,
|
||||
&memo.CreatorID,
|
||||
&memo.CreatedTs,
|
||||
&memo.UpdatedTs,
|
||||
&memo.Content,
|
||||
&memo.RowStatus,
|
||||
&memoRaw.ID,
|
||||
&memoRaw.CreatorID,
|
||||
&memoRaw.CreatedTs,
|
||||
&memoRaw.UpdatedTs,
|
||||
&memoRaw.Content,
|
||||
&memoRaw.RowStatus,
|
||||
); err != nil {
|
||||
return nil, FormatError(err)
|
||||
}
|
||||
|
||||
list = append(list, &memo)
|
||||
memoRawList = append(memoRawList, &memoRaw)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, FormatError(err)
|
||||
}
|
||||
|
||||
return list, nil
|
||||
return memoRawList, nil
|
||||
}
|
||||
|
||||
func deleteMemo(db *DB, delete *api.MemoDelete) error {
|
||||
@ -206,3 +263,19 @@ func deleteMemo(db *DB, delete *api.MemoDelete) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) composeMemo(raw *memoRaw) (*api.Memo, error) {
|
||||
memo := raw.toMemo()
|
||||
|
||||
memoOrganizer, err := s.FindMemoOrganizer(&api.MemoOrganizerFind{
|
||||
MemoID: memo.ID,
|
||||
UserID: memo.CreatorID,
|
||||
})
|
||||
if err != nil && common.ErrorCode(err) != common.NotFound {
|
||||
return nil, err
|
||||
} else if memoOrganizer != nil {
|
||||
memo.Pinned = memoOrganizer.Pinned
|
||||
}
|
||||
|
||||
return memo, nil
|
||||
}
|
||||
|
117
store/memo_organizer.go
Normal file
117
store/memo_organizer.go
Normal file
@ -0,0 +1,117 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"memos/api"
|
||||
"memos/common"
|
||||
)
|
||||
|
||||
// memoOrganizerRaw is the store model for an MemoOrganizer.
|
||||
// Fields have exactly the same meanings as MemoOrganizer.
|
||||
type memoOrganizerRaw struct {
|
||||
ID int
|
||||
|
||||
// Domain specific fields
|
||||
MemoID int
|
||||
UserID int
|
||||
Pinned bool
|
||||
}
|
||||
|
||||
func (raw *memoOrganizerRaw) toMemoOrganizer() *api.MemoOrganizer {
|
||||
return &api.MemoOrganizer{
|
||||
ID: raw.ID,
|
||||
|
||||
MemoID: raw.MemoID,
|
||||
UserID: raw.UserID,
|
||||
Pinned: raw.Pinned,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store) FindMemoOrganizer(find *api.MemoOrganizerFind) (*api.MemoOrganizer, error) {
|
||||
memoOrganizerRaw, err := findMemoOrganizer(s.db, find)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
memoOrganizer := memoOrganizerRaw.toMemoOrganizer()
|
||||
|
||||
return memoOrganizer, nil
|
||||
}
|
||||
|
||||
func (s *Store) UpsertMemoOrganizer(upsert *api.MemoOrganizerUpsert) error {
|
||||
err := upsertMemoOrganizer(s.db, upsert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func findMemoOrganizer(db *DB, find *api.MemoOrganizerFind) (*memoOrganizerRaw, error) {
|
||||
row, err := db.Db.Query(`
|
||||
SELECT
|
||||
id,
|
||||
memo_id,
|
||||
user_id,
|
||||
pinned
|
||||
FROM memo_organizer
|
||||
WHERE memo_id = ? AND user_id = ?
|
||||
`, find.MemoID, find.UserID)
|
||||
if err != nil {
|
||||
return nil, FormatError(err)
|
||||
}
|
||||
defer row.Close()
|
||||
|
||||
if !row.Next() {
|
||||
return nil, &common.Error{Code: common.NotFound, Err: fmt.Errorf("not found")}
|
||||
}
|
||||
|
||||
var memoOrganizerRaw memoOrganizerRaw
|
||||
if err := row.Scan(
|
||||
&memoOrganizerRaw.ID,
|
||||
&memoOrganizerRaw.MemoID,
|
||||
&memoOrganizerRaw.UserID,
|
||||
&memoOrganizerRaw.Pinned,
|
||||
); err != nil {
|
||||
return nil, FormatError(err)
|
||||
}
|
||||
|
||||
return &memoOrganizerRaw, nil
|
||||
}
|
||||
|
||||
func upsertMemoOrganizer(db *DB, upsert *api.MemoOrganizerUpsert) error {
|
||||
row, err := db.Db.Query(`
|
||||
INSERT INTO memo_organizer (
|
||||
memo_id,
|
||||
user_id,
|
||||
pinned
|
||||
)
|
||||
VALUES (?, ?, ?)
|
||||
ON CONFLICT(memo_id, user_id) DO UPDATE
|
||||
SET
|
||||
pinned = EXCLUDED.pinned
|
||||
RETURNING id, memo_id, user_id, pinned
|
||||
`,
|
||||
upsert.MemoID,
|
||||
upsert.UserID,
|
||||
upsert.Pinned,
|
||||
)
|
||||
if err != nil {
|
||||
return FormatError(err)
|
||||
}
|
||||
|
||||
defer row.Close()
|
||||
|
||||
row.Next()
|
||||
var memoOrganizer api.MemoOrganizer
|
||||
if err := row.Scan(
|
||||
&memoOrganizer.ID,
|
||||
&memoOrganizer.MemoID,
|
||||
&memoOrganizer.UserID,
|
||||
&memoOrganizer.Pinned,
|
||||
); err != nil {
|
||||
return FormatError(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
DROP TABLE IF EXISTS `memo_organizer`;
|
||||
DROP TABLE IF EXISTS `memo`;
|
||||
DROP TABLE IF EXISTS `shortcut`;
|
||||
DROP TABLE IF EXISTS `resource`;
|
||||
|
@ -3,6 +3,8 @@ CREATE TABLE user (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||
-- allowed row status are 'NORMAL', 'ARCHIVED'.
|
||||
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
role TEXT NOT NULL CHECK (role IN ('OWNER', 'USER')) DEFAULT 'USER',
|
||||
name TEXT NOT NULL,
|
||||
@ -33,10 +35,9 @@ CREATE TABLE memo (
|
||||
creator_id INTEGER NOT NULL,
|
||||
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||
-- allowed row status are 'NORMAL', 'ARCHIVED', 'HIDDEN'.
|
||||
row_status TEXT NOT NULL DEFAULT 'NORMAL',
|
||||
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||
content TEXT NOT NULL DEFAULT '',
|
||||
FOREIGN KEY(creator_id) REFERENCES users(id)
|
||||
FOREIGN KEY(creator_id) REFERENCES user(id)
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
@ -56,17 +57,32 @@ WHERE
|
||||
rowid = old.rowid;
|
||||
END;
|
||||
|
||||
-- memo_organizer
|
||||
CREATE TABLE memo_organizer (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
memo_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
pinned INTEGER NOT NULL CHECK (pinned IN (0, 1)) DEFAULT 0,
|
||||
FOREIGN KEY(memo_id) REFERENCES memo(id),
|
||||
FOREIGN KEY(user_id) REFERENCES user(id),
|
||||
UNIQUE(memo_id, user_id)
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
sqlite_sequence (name, seq)
|
||||
VALUES
|
||||
('memo_organizer', 100);
|
||||
|
||||
-- shortcut
|
||||
CREATE TABLE shortcut (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
creator_id INTEGER NOT NULL,
|
||||
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||
title TEXT NOT NULL DEFAULT '',
|
||||
payload TEXT NOT NULL DEFAULT '{}',
|
||||
-- allowed row status are 'NORMAL', 'ARCHIVED'.
|
||||
row_status TEXT NOT NULL DEFAULT 'NORMAL',
|
||||
FOREIGN KEY(creator_id) REFERENCES users(id)
|
||||
FOREIGN KEY(creator_id) REFERENCES user(id)
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
@ -96,7 +112,7 @@ CREATE TABLE resource (
|
||||
blob BLOB NOT NULL,
|
||||
type TEXT NOT NULL DEFAULT '',
|
||||
size INTEGER NOT NULL DEFAULT 0,
|
||||
FOREIGN KEY(creator_id) REFERENCES users(id)
|
||||
FOREIGN KEY(creator_id) REFERENCES user(id)
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
|
@ -7,22 +7,63 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// resourceRaw is the store model for an Resource.
|
||||
// Fields have exactly the same meanings as Resource.
|
||||
type resourceRaw struct {
|
||||
ID int
|
||||
|
||||
// Standard fields
|
||||
CreatorID int
|
||||
CreatedTs int64
|
||||
UpdatedTs int64
|
||||
|
||||
// Domain specific fields
|
||||
Filename string
|
||||
Blob []byte
|
||||
Type string
|
||||
Size int64
|
||||
}
|
||||
|
||||
func (raw *resourceRaw) toResource() *api.Resource {
|
||||
return &api.Resource{
|
||||
ID: raw.ID,
|
||||
|
||||
// Standard fields
|
||||
CreatorID: raw.CreatorID,
|
||||
CreatedTs: raw.CreatedTs,
|
||||
UpdatedTs: raw.UpdatedTs,
|
||||
|
||||
// Domain specific fields
|
||||
Filename: raw.Filename,
|
||||
Blob: raw.Blob,
|
||||
Type: raw.Type,
|
||||
Size: raw.Size,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store) CreateResource(create *api.ResourceCreate) (*api.Resource, error) {
|
||||
resource, err := createResource(s.db, create)
|
||||
resourceRaw, err := createResource(s.db, create)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resource := resourceRaw.toResource()
|
||||
|
||||
return resource, nil
|
||||
}
|
||||
|
||||
func (s *Store) FindResourceList(find *api.ResourceFind) ([]*api.Resource, error) {
|
||||
list, err := findResourceList(s.db, find)
|
||||
resourceRawList, err := findResourceList(s.db, find)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return list, nil
|
||||
resourceList := []*api.Resource{}
|
||||
for _, raw := range resourceRawList {
|
||||
resourceList = append(resourceList, raw.toResource())
|
||||
}
|
||||
|
||||
return resourceList, nil
|
||||
}
|
||||
|
||||
func (s *Store) FindResource(find *api.ResourceFind) (*api.Resource, error) {
|
||||
@ -35,7 +76,9 @@ func (s *Store) FindResource(find *api.ResourceFind) (*api.Resource, error) {
|
||||
return nil, &common.Error{Code: common.NotFound, Err: fmt.Errorf("not found")}
|
||||
}
|
||||
|
||||
return list[0], nil
|
||||
resource := list[0].toResource()
|
||||
|
||||
return resource, nil
|
||||
}
|
||||
|
||||
func (s *Store) DeleteResource(delete *api.ResourceDelete) error {
|
||||
@ -47,7 +90,7 @@ func (s *Store) DeleteResource(delete *api.ResourceDelete) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func createResource(db *DB, create *api.ResourceCreate) (*api.Resource, error) {
|
||||
func createResource(db *DB, create *api.ResourceCreate) (*resourceRaw, error) {
|
||||
row, err := db.Db.Query(`
|
||||
INSERT INTO resource (
|
||||
filename,
|
||||
@ -70,27 +113,24 @@ func createResource(db *DB, create *api.ResourceCreate) (*api.Resource, error) {
|
||||
}
|
||||
defer row.Close()
|
||||
|
||||
if !row.Next() {
|
||||
return nil, &common.Error{Code: common.NotFound, Err: fmt.Errorf("not found")}
|
||||
}
|
||||
|
||||
var resource api.Resource
|
||||
row.Next()
|
||||
var resourceRaw resourceRaw
|
||||
if err := row.Scan(
|
||||
&resource.ID,
|
||||
&resource.Filename,
|
||||
&resource.Blob,
|
||||
&resource.Type,
|
||||
&resource.Size,
|
||||
&resource.CreatedTs,
|
||||
&resource.UpdatedTs,
|
||||
&resourceRaw.ID,
|
||||
&resourceRaw.Filename,
|
||||
&resourceRaw.Blob,
|
||||
&resourceRaw.Type,
|
||||
&resourceRaw.Size,
|
||||
&resourceRaw.CreatedTs,
|
||||
&resourceRaw.UpdatedTs,
|
||||
); err != nil {
|
||||
return nil, FormatError(err)
|
||||
}
|
||||
|
||||
return &resource, nil
|
||||
return &resourceRaw, nil
|
||||
}
|
||||
|
||||
func findResourceList(db *DB, find *api.ResourceFind) ([]*api.Resource, error) {
|
||||
func findResourceList(db *DB, find *api.ResourceFind) ([]*resourceRaw, error) {
|
||||
where, args := []string{"1 = 1"}, []interface{}{}
|
||||
|
||||
if v := find.ID; v != nil {
|
||||
@ -121,29 +161,29 @@ func findResourceList(db *DB, find *api.ResourceFind) ([]*api.Resource, error) {
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
list := make([]*api.Resource, 0)
|
||||
resourceRawList := make([]*resourceRaw, 0)
|
||||
for rows.Next() {
|
||||
var resource api.Resource
|
||||
var resourceRaw resourceRaw
|
||||
if err := rows.Scan(
|
||||
&resource.ID,
|
||||
&resource.Filename,
|
||||
&resource.Blob,
|
||||
&resource.Type,
|
||||
&resource.Size,
|
||||
&resource.CreatedTs,
|
||||
&resource.UpdatedTs,
|
||||
&resourceRaw.ID,
|
||||
&resourceRaw.Filename,
|
||||
&resourceRaw.Blob,
|
||||
&resourceRaw.Type,
|
||||
&resourceRaw.Size,
|
||||
&resourceRaw.CreatedTs,
|
||||
&resourceRaw.UpdatedTs,
|
||||
); err != nil {
|
||||
return nil, FormatError(err)
|
||||
}
|
||||
|
||||
list = append(list, &resource)
|
||||
resourceRawList = append(resourceRawList, &resourceRaw)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, FormatError(err)
|
||||
}
|
||||
|
||||
return list, nil
|
||||
return resourceRawList, nil
|
||||
}
|
||||
|
||||
func deleteResource(db *DB, delete *api.ResourceDelete) error {
|
||||
|
@ -1,3 +1,4 @@
|
||||
DELETE FROM memo_organizer;
|
||||
DELETE FROM resource;
|
||||
DELETE FROM shortcut;
|
||||
DELETE FROM memo;
|
||||
|
@ -12,6 +12,6 @@ VALUES
|
||||
'guest',
|
||||
'guest@example.com',
|
||||
'guest_open_id',
|
||||
-- "secret"
|
||||
-- raw password: secret
|
||||
'$2a$14$ajq8Q7fbtFRQvXpdCq7Jcuy.Rx1h/L4J60Otx.gyNLbAYctGMJ9tK'
|
||||
);
|
||||
|
@ -1,23 +1,25 @@
|
||||
INSERT INTO
|
||||
memo (
|
||||
`id`,
|
||||
`content`,
|
||||
`creator_id`
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
101,
|
||||
'#memos 👋 Welcome to memos',
|
||||
101
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
memo (
|
||||
`id`,
|
||||
`content`,
|
||||
`creator_id`,
|
||||
`row_status`
|
||||
`creator_id`
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
102,
|
||||
'好好学习,天天向上。',
|
||||
101,
|
||||
'ARCHIVED'
|
||||
101
|
||||
);
|
||||
|
12
store/seed/10003__memo_organizer.sql
Normal file
12
store/seed/10003__memo_organizer.sql
Normal file
@ -0,0 +1,12 @@
|
||||
INSERT INTO
|
||||
memo_organizer (
|
||||
`memo_id`,
|
||||
`user_id`,
|
||||
`pinned`
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
102,
|
||||
101,
|
||||
1
|
||||
);
|
@ -7,30 +7,69 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// shortcutRaw is the store model for an Shortcut.
|
||||
// Fields have exactly the same meanings as Shortcut.
|
||||
type shortcutRaw struct {
|
||||
ID int
|
||||
|
||||
// Standard fields
|
||||
RowStatus api.RowStatus
|
||||
CreatorID int
|
||||
CreatedTs int64
|
||||
UpdatedTs int64
|
||||
|
||||
// Domain specific fields
|
||||
Title string
|
||||
Payload string
|
||||
}
|
||||
|
||||
func (raw *shortcutRaw) toShortcut() *api.Shortcut {
|
||||
return &api.Shortcut{
|
||||
ID: raw.ID,
|
||||
|
||||
RowStatus: raw.RowStatus,
|
||||
CreatorID: raw.CreatorID,
|
||||
CreatedTs: raw.CreatedTs,
|
||||
UpdatedTs: raw.UpdatedTs,
|
||||
|
||||
Title: raw.Title,
|
||||
Payload: raw.Payload,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store) CreateShortcut(create *api.ShortcutCreate) (*api.Shortcut, error) {
|
||||
shortcut, err := createShortcut(s.db, create)
|
||||
shortcutRaw, err := createShortcut(s.db, create)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
shortcut := shortcutRaw.toShortcut()
|
||||
|
||||
return shortcut, nil
|
||||
}
|
||||
|
||||
func (s *Store) PatchShortcut(patch *api.ShortcutPatch) (*api.Shortcut, error) {
|
||||
shortcut, err := patchShortcut(s.db, patch)
|
||||
shortcutRaw, err := patchShortcut(s.db, patch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
shortcut := shortcutRaw.toShortcut()
|
||||
|
||||
return shortcut, nil
|
||||
}
|
||||
|
||||
func (s *Store) FindShortcutList(find *api.ShortcutFind) ([]*api.Shortcut, error) {
|
||||
list, err := findShortcutList(s.db, find)
|
||||
shortcutRawList, err := findShortcutList(s.db, find)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list := []*api.Shortcut{}
|
||||
for _, raw := range shortcutRawList {
|
||||
list = append(list, raw.toShortcut())
|
||||
}
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
@ -44,7 +83,9 @@ func (s *Store) FindShortcut(find *api.ShortcutFind) (*api.Shortcut, error) {
|
||||
return nil, &common.Error{Code: common.NotFound, Err: fmt.Errorf("not found")}
|
||||
}
|
||||
|
||||
return list[0], nil
|
||||
shortcut := list[0].toShortcut()
|
||||
|
||||
return shortcut, nil
|
||||
}
|
||||
|
||||
func (s *Store) DeleteShortcut(delete *api.ShortcutDelete) error {
|
||||
@ -56,7 +97,7 @@ func (s *Store) DeleteShortcut(delete *api.ShortcutDelete) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func createShortcut(db *DB, create *api.ShortcutCreate) (*api.Shortcut, error) {
|
||||
func createShortcut(db *DB, create *api.ShortcutCreate) (*shortcutRaw, error) {
|
||||
row, err := db.Db.Query(`
|
||||
INSERT INTO shortcut (
|
||||
title,
|
||||
@ -70,30 +111,29 @@ func createShortcut(db *DB, create *api.ShortcutCreate) (*api.Shortcut, error) {
|
||||
create.Payload,
|
||||
create.CreatorID,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, FormatError(err)
|
||||
}
|
||||
defer row.Close()
|
||||
|
||||
row.Next()
|
||||
var shortcut api.Shortcut
|
||||
var shortcutRaw shortcutRaw
|
||||
if err := row.Scan(
|
||||
&shortcut.ID,
|
||||
&shortcut.Title,
|
||||
&shortcut.Payload,
|
||||
&shortcut.CreatorID,
|
||||
&shortcut.CreatedTs,
|
||||
&shortcut.UpdatedTs,
|
||||
&shortcut.RowStatus,
|
||||
&shortcutRaw.ID,
|
||||
&shortcutRaw.Title,
|
||||
&shortcutRaw.Payload,
|
||||
&shortcutRaw.CreatorID,
|
||||
&shortcutRaw.CreatedTs,
|
||||
&shortcutRaw.UpdatedTs,
|
||||
&shortcutRaw.RowStatus,
|
||||
); err != nil {
|
||||
return nil, FormatError(err)
|
||||
}
|
||||
|
||||
return &shortcut, nil
|
||||
return &shortcutRaw, nil
|
||||
}
|
||||
|
||||
func patchShortcut(db *DB, patch *api.ShortcutPatch) (*api.Shortcut, error) {
|
||||
func patchShortcut(db *DB, patch *api.ShortcutPatch) (*shortcutRaw, error) {
|
||||
set, args := []string{}, []interface{}{}
|
||||
|
||||
if v := patch.Title; v != nil {
|
||||
@ -123,22 +163,22 @@ func patchShortcut(db *DB, patch *api.ShortcutPatch) (*api.Shortcut, error) {
|
||||
return nil, &common.Error{Code: common.NotFound, Err: fmt.Errorf("not found")}
|
||||
}
|
||||
|
||||
var shortcut api.Shortcut
|
||||
var shortcutRaw shortcutRaw
|
||||
if err := row.Scan(
|
||||
&shortcut.ID,
|
||||
&shortcut.Title,
|
||||
&shortcut.Payload,
|
||||
&shortcut.CreatedTs,
|
||||
&shortcut.UpdatedTs,
|
||||
&shortcut.RowStatus,
|
||||
&shortcutRaw.ID,
|
||||
&shortcutRaw.Title,
|
||||
&shortcutRaw.Payload,
|
||||
&shortcutRaw.CreatedTs,
|
||||
&shortcutRaw.UpdatedTs,
|
||||
&shortcutRaw.RowStatus,
|
||||
); err != nil {
|
||||
return nil, FormatError(err)
|
||||
}
|
||||
|
||||
return &shortcut, nil
|
||||
return &shortcutRaw, nil
|
||||
}
|
||||
|
||||
func findShortcutList(db *DB, find *api.ShortcutFind) ([]*api.Shortcut, error) {
|
||||
func findShortcutList(db *DB, find *api.ShortcutFind) ([]*shortcutRaw, error) {
|
||||
where, args := []string{"1 = 1"}, []interface{}{}
|
||||
|
||||
if v := find.ID; v != nil {
|
||||
@ -169,29 +209,29 @@ func findShortcutList(db *DB, find *api.ShortcutFind) ([]*api.Shortcut, error) {
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
list := make([]*api.Shortcut, 0)
|
||||
shortcutRawList := make([]*shortcutRaw, 0)
|
||||
for rows.Next() {
|
||||
var shortcut api.Shortcut
|
||||
var shortcutRaw shortcutRaw
|
||||
if err := rows.Scan(
|
||||
&shortcut.ID,
|
||||
&shortcut.Title,
|
||||
&shortcut.Payload,
|
||||
&shortcut.CreatorID,
|
||||
&shortcut.CreatedTs,
|
||||
&shortcut.UpdatedTs,
|
||||
&shortcut.RowStatus,
|
||||
&shortcutRaw.ID,
|
||||
&shortcutRaw.Title,
|
||||
&shortcutRaw.Payload,
|
||||
&shortcutRaw.CreatorID,
|
||||
&shortcutRaw.CreatedTs,
|
||||
&shortcutRaw.UpdatedTs,
|
||||
&shortcutRaw.RowStatus,
|
||||
); err != nil {
|
||||
return nil, FormatError(err)
|
||||
}
|
||||
|
||||
list = append(list, &shortcut)
|
||||
shortcutRawList = append(shortcutRawList, &shortcutRaw)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, FormatError(err)
|
||||
}
|
||||
|
||||
return list, nil
|
||||
return shortcutRawList, nil
|
||||
}
|
||||
|
||||
func deleteShortcut(db *DB, delete *api.ShortcutDelete) error {
|
||||
|
134
store/user.go
134
store/user.go
@ -7,30 +7,73 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// userRaw is the store model for an User.
|
||||
// Fields have exactly the same meanings as User.
|
||||
type userRaw struct {
|
||||
ID int
|
||||
|
||||
// Standard fields
|
||||
RowStatus api.RowStatus
|
||||
CreatedTs int64
|
||||
UpdatedTs int64
|
||||
|
||||
// Domain specific fields
|
||||
Email string
|
||||
Role api.Role
|
||||
Name string
|
||||
PasswordHash string
|
||||
OpenID string
|
||||
}
|
||||
|
||||
func (raw *userRaw) toUser() *api.User {
|
||||
return &api.User{
|
||||
ID: raw.ID,
|
||||
|
||||
RowStatus: raw.RowStatus,
|
||||
CreatedTs: raw.CreatedTs,
|
||||
UpdatedTs: raw.UpdatedTs,
|
||||
|
||||
Email: raw.Email,
|
||||
Role: raw.Role,
|
||||
Name: raw.Name,
|
||||
PasswordHash: raw.PasswordHash,
|
||||
OpenID: raw.OpenID,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store) CreateUser(create *api.UserCreate) (*api.User, error) {
|
||||
user, err := createUser(s.db, create)
|
||||
userRaw, err := createUser(s.db, create)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user := userRaw.toUser()
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (s *Store) PatchUser(patch *api.UserPatch) (*api.User, error) {
|
||||
user, err := patchUser(s.db, patch)
|
||||
userRaw, err := patchUser(s.db, patch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user := userRaw.toUser()
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (s *Store) FindUserList(find *api.UserFind) ([]*api.User, error) {
|
||||
list, err := findUserList(s.db, find)
|
||||
userRawList, err := findUserList(s.db, find)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list := []*api.User{}
|
||||
for _, raw := range userRawList {
|
||||
list = append(list, raw.toUser())
|
||||
}
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
@ -46,10 +89,12 @@ func (s *Store) FindUser(find *api.UserFind) (*api.User, error) {
|
||||
return nil, &common.Error{Code: common.Conflict, Err: fmt.Errorf("found %d users with filter %+v, expect 1. ", len(list), find)}
|
||||
}
|
||||
|
||||
return list[0], nil
|
||||
user := list[0].toUser()
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func createUser(db *DB, create *api.UserCreate) (*api.User, error) {
|
||||
func createUser(db *DB, create *api.UserCreate) (*userRaw, error) {
|
||||
row, err := db.Db.Query(`
|
||||
INSERT INTO user (
|
||||
email,
|
||||
@ -73,37 +118,40 @@ func createUser(db *DB, create *api.UserCreate) (*api.User, error) {
|
||||
defer row.Close()
|
||||
|
||||
row.Next()
|
||||
var user api.User
|
||||
var userRaw userRaw
|
||||
if err := row.Scan(
|
||||
&user.ID,
|
||||
&user.Email,
|
||||
&user.Role,
|
||||
&user.Name,
|
||||
&user.PasswordHash,
|
||||
&user.OpenID,
|
||||
&user.CreatedTs,
|
||||
&user.UpdatedTs,
|
||||
&userRaw.ID,
|
||||
&userRaw.Email,
|
||||
&userRaw.Role,
|
||||
&userRaw.Name,
|
||||
&userRaw.PasswordHash,
|
||||
&userRaw.OpenID,
|
||||
&userRaw.CreatedTs,
|
||||
&userRaw.UpdatedTs,
|
||||
); err != nil {
|
||||
return nil, FormatError(err)
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
return &userRaw, nil
|
||||
}
|
||||
|
||||
func patchUser(db *DB, patch *api.UserPatch) (*api.User, error) {
|
||||
func patchUser(db *DB, patch *api.UserPatch) (*userRaw, error) {
|
||||
set, args := []string{}, []interface{}{}
|
||||
|
||||
if v := patch.RowStatus; v != nil {
|
||||
set, args = append(set, "row_status = ?"), append(args, *v)
|
||||
}
|
||||
if v := patch.Email; v != nil {
|
||||
set, args = append(set, "email = ?"), append(args, v)
|
||||
set, args = append(set, "email = ?"), append(args, *v)
|
||||
}
|
||||
if v := patch.Name; v != nil {
|
||||
set, args = append(set, "name = ?"), append(args, v)
|
||||
set, args = append(set, "name = ?"), append(args, *v)
|
||||
}
|
||||
if v := patch.PasswordHash; v != nil {
|
||||
set, args = append(set, "password_hash = ?"), append(args, v)
|
||||
set, args = append(set, "password_hash = ?"), append(args, *v)
|
||||
}
|
||||
if v := patch.OpenID; v != nil {
|
||||
set, args = append(set, "open_id = ?"), append(args, v)
|
||||
set, args = append(set, "open_id = ?"), append(args, *v)
|
||||
}
|
||||
|
||||
args = append(args, patch.ID)
|
||||
@ -120,27 +168,27 @@ func patchUser(db *DB, patch *api.UserPatch) (*api.User, error) {
|
||||
defer row.Close()
|
||||
|
||||
if row.Next() {
|
||||
var user api.User
|
||||
var userRaw userRaw
|
||||
if err := row.Scan(
|
||||
&user.ID,
|
||||
&user.Email,
|
||||
&user.Role,
|
||||
&user.Name,
|
||||
&user.PasswordHash,
|
||||
&user.OpenID,
|
||||
&user.CreatedTs,
|
||||
&user.UpdatedTs,
|
||||
&userRaw.ID,
|
||||
&userRaw.Email,
|
||||
&userRaw.Role,
|
||||
&userRaw.Name,
|
||||
&userRaw.PasswordHash,
|
||||
&userRaw.OpenID,
|
||||
&userRaw.CreatedTs,
|
||||
&userRaw.UpdatedTs,
|
||||
); err != nil {
|
||||
return nil, FormatError(err)
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
return &userRaw, nil
|
||||
}
|
||||
|
||||
return nil, &common.Error{Code: common.NotFound, Err: fmt.Errorf("user ID not found: %d", patch.ID)}
|
||||
}
|
||||
|
||||
func findUserList(db *DB, find *api.UserFind) ([]*api.User, error) {
|
||||
func findUserList(db *DB, find *api.UserFind) ([]*userRaw, error) {
|
||||
where, args := []string{"1 = 1"}, []interface{}{}
|
||||
|
||||
if v := find.ID; v != nil {
|
||||
@ -178,29 +226,29 @@ func findUserList(db *DB, find *api.UserFind) ([]*api.User, error) {
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
list := make([]*api.User, 0)
|
||||
userRawList := make([]*userRaw, 0)
|
||||
for rows.Next() {
|
||||
var user api.User
|
||||
var userRaw userRaw
|
||||
if err := rows.Scan(
|
||||
&user.ID,
|
||||
&user.Email,
|
||||
&user.Role,
|
||||
&user.Name,
|
||||
&user.PasswordHash,
|
||||
&user.OpenID,
|
||||
&user.CreatedTs,
|
||||
&user.UpdatedTs,
|
||||
&userRaw.ID,
|
||||
&userRaw.Email,
|
||||
&userRaw.Role,
|
||||
&userRaw.Name,
|
||||
&userRaw.PasswordHash,
|
||||
&userRaw.OpenID,
|
||||
&userRaw.CreatedTs,
|
||||
&userRaw.UpdatedTs,
|
||||
); err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, FormatError(err)
|
||||
}
|
||||
|
||||
list = append(list, &user)
|
||||
userRawList = append(userRawList, &userRaw)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, FormatError(err)
|
||||
}
|
||||
|
||||
return list, nil
|
||||
return userRawList, nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user