add feed follows
This commit is contained in:
parent
6035a17df9
commit
e18ab0aa6e
71
handler_feed_follows.go
Normal file
71
handler_feed_follows.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.rpuzonas.com/rpuzonas/go-rss-aggregator/internal/database"
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func (apiCfg *apiConfig) handlerFollowFeed(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
|
type parameters struct {
|
||||||
|
FeedID int64 `json:"feed_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder := json.NewDecoder(r.Body)
|
||||||
|
params := parameters{}
|
||||||
|
err := decoder.Decode(¶ms)
|
||||||
|
if err != nil {
|
||||||
|
respondWithError(w, 400, fmt.Sprintf("Error parsing JSON: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().UTC()
|
||||||
|
feedFollow, err := apiCfg.DB.CreateFeedFollow(r.Context(), database.CreateFeedFollowParams{
|
||||||
|
UpdatedAt: now,
|
||||||
|
CreatedAt: now,
|
||||||
|
UserID: user.ID,
|
||||||
|
FeedID: params.FeedID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
respondWithError(w, 400, fmt.Sprintf("Couldn't follow feed: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
respondWithJSON(w, 200, databaseFeedFollowToFeedFollow(feedFollow))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (apiCfg *apiConfig) handlerGetFeedFollows(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
|
feedFollows, err := apiCfg.DB.GetFeedFollowsByUser(r.Context(), user.ID)
|
||||||
|
if err != nil {
|
||||||
|
respondWithError(w, 400, fmt.Sprintf("Couldn't list feed follows: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
respondWithJSON(w, 200, databaseFolllowFeedsToFollowFeeds(feedFollows))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (apiCfg *apiConfig) handlerDeleteFeedFollow(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
|
feedFollowIDStr := chi.URLParam(r, "feedFollowID");
|
||||||
|
feedFollowID, err := strconv.ParseInt(feedFollowIDStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
respondWithError(w, 400, fmt.Sprintf("Failed to parse feed id: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = apiCfg.DB.DeleteFeedFollow(r.Context(), database.DeleteFeedFollowParams{
|
||||||
|
UserID: user.ID,
|
||||||
|
ID: feedFollowID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
respondWithError(w, 400, fmt.Sprintf("Failed to delete feed follow: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
respondWithJSON(w, 200, struct{}{})
|
||||||
|
}
|
3
http-requests/delete-feed-follow.http
Normal file
3
http-requests/delete-feed-follow.http
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
DELETE http://localhost:8080/v1/feed-follows/10
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: ApiKey aec0a1cbc6658af07e0d3ed67079a46928744b7787170a913abea4c68e9af702bbf2593bb8283b1574cf7b2e0d60df1abbd3e619c80e483d666f219ea71c72374e66b372a173e1407087c5cdeecf7dc66f34cdf59a0b6c448a86b6954ffc2693185550cd203625c8fa1eb0388e0834ee70cecec0dc199197a6e7229d28659fb6929c8410d74f0336a73ab62be5486451a270bcdbc37b0f73cad6654bc057fe28754e151330bbbc22c9e8d645015b81f035f95ec5b21b064965b65123f3a0705df4ab83a963d6a3353226cb7e9eeecdfb9631299e3068f9e28220e58bd7a69389a8697bee67501017cbde6fb2c809b136496533f40efed60be51e2b44899b8ade
|
7
http-requests/follow-feed.http
Normal file
7
http-requests/follow-feed.http
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
POST http://localhost:8080/v1/feed-follows
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: ApiKey aec0a1cbc6658af07e0d3ed67079a46928744b7787170a913abea4c68e9af702bbf2593bb8283b1574cf7b2e0d60df1abbd3e619c80e483d666f219ea71c72374e66b372a173e1407087c5cdeecf7dc66f34cdf59a0b6c448a86b6954ffc2693185550cd203625c8fa1eb0388e0834ee70cecec0dc199197a6e7229d28659fb6929c8410d74f0336a73ab62be5486451a270bcdbc37b0f73cad6654bc057fe28754e151330bbbc22c9e8d645015b81f035f95ec5b21b064965b65123f3a0705df4ab83a963d6a3353226cb7e9eeecdfb9631299e3068f9e28220e58bd7a69389a8697bee67501017cbde6fb2c809b136496533f40efed60be51e2b44899b8ade
|
||||||
|
|
||||||
|
{
|
||||||
|
"feed_id": 4
|
||||||
|
}
|
3
http-requests/list-feed-follows.http
Normal file
3
http-requests/list-feed-follows.http
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
GET http://localhost:8080/v1/feed-follows
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: ApiKey aec0a1cbc6658af07e0d3ed67079a46928744b7787170a913abea4c68e9af702bbf2593bb8283b1574cf7b2e0d60df1abbd3e619c80e483d666f219ea71c72374e66b372a173e1407087c5cdeecf7dc66f34cdf59a0b6c448a86b6954ffc2693185550cd203625c8fa1eb0388e0834ee70cecec0dc199197a6e7229d28659fb6929c8410d74f0336a73ab62be5486451a270bcdbc37b0f73cad6654bc057fe28754e151330bbbc22c9e8d645015b81f035f95ec5b21b064965b65123f3a0705df4ab83a963d6a3353226cb7e9eeecdfb9631299e3068f9e28220e58bd7a69389a8697bee67501017cbde6fb2c809b136496533f40efed60be51e2b44899b8ade
|
89
internal/database/feed_follows.sql.go
Normal file
89
internal/database/feed_follows.sql.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.21.0
|
||||||
|
// source: feed_follows.sql
|
||||||
|
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const createFeedFollow = `-- name: CreateFeedFollow :one
|
||||||
|
INSERT INTO feed_follows (created_at, updated_at, user_id, feed_id)
|
||||||
|
VALUES (?, ?, ?, ?)
|
||||||
|
RETURNING id, created_at, updated_at, user_id, feed_id
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateFeedFollowParams struct {
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
UserID int64
|
||||||
|
FeedID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateFeedFollow(ctx context.Context, arg CreateFeedFollowParams) (FeedFollow, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, createFeedFollow,
|
||||||
|
arg.CreatedAt,
|
||||||
|
arg.UpdatedAt,
|
||||||
|
arg.UserID,
|
||||||
|
arg.FeedID,
|
||||||
|
)
|
||||||
|
var i FeedFollow
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
&i.UserID,
|
||||||
|
&i.FeedID,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteFeedFollow = `-- name: DeleteFeedFollow :exec
|
||||||
|
DELETE FROM feed_follows WHERE id = ? AND user_id = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
type DeleteFeedFollowParams struct {
|
||||||
|
ID int64
|
||||||
|
UserID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) DeleteFeedFollow(ctx context.Context, arg DeleteFeedFollowParams) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteFeedFollow, arg.ID, arg.UserID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFeedFollowsByUser = `-- name: GetFeedFollowsByUser :many
|
||||||
|
SELECT id, created_at, updated_at, user_id, feed_id FROM feed_follows WHERE user_id = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetFeedFollowsByUser(ctx context.Context, userID int64) ([]FeedFollow, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getFeedFollowsByUser, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []FeedFollow
|
||||||
|
for rows.Next() {
|
||||||
|
var i FeedFollow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
&i.UserID,
|
||||||
|
&i.FeedID,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
@ -18,6 +18,14 @@ type Feed struct {
|
|||||||
UserID int64
|
UserID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FeedFollow struct {
|
||||||
|
ID int64
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
UserID int64
|
||||||
|
FeedID int64
|
||||||
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID int64
|
ID int64
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
|
4
main.go
4
main.go
@ -40,6 +40,10 @@ func createV1Router(cfg *apiConfig) chi.Router {
|
|||||||
v1Router.Post("/feeds", cfg.middlewareAuth(cfg.handlerCreateFeed))
|
v1Router.Post("/feeds", cfg.middlewareAuth(cfg.handlerCreateFeed))
|
||||||
v1Router.Get("/feeds", cfg.handlerGetAllFeeds)
|
v1Router.Get("/feeds", cfg.handlerGetAllFeeds)
|
||||||
|
|
||||||
|
v1Router.Post("/feed-follows", cfg.middlewareAuth(cfg.handlerFollowFeed))
|
||||||
|
v1Router.Delete("/feed-follows/{feedFollowID}", cfg.middlewareAuth(cfg.handlerDeleteFeedFollow))
|
||||||
|
v1Router.Get("/feed-follows", cfg.middlewareAuth(cfg.handlerGetFeedFollows))
|
||||||
|
|
||||||
return v1Router
|
return v1Router
|
||||||
}
|
}
|
||||||
|
|
||||||
|
26
models.go
26
models.go
@ -23,6 +23,14 @@ type Feed struct {
|
|||||||
UserID int64 `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FeedFollow struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
FeedID int64 `json:"feed_id"`
|
||||||
|
}
|
||||||
|
|
||||||
func databaseUserToUser(dbUser database.User) User {
|
func databaseUserToUser(dbUser database.User) User {
|
||||||
return User{
|
return User{
|
||||||
ID: dbUser.ID,
|
ID: dbUser.ID,
|
||||||
@ -51,3 +59,21 @@ func databaseFeedsToFeeds(dbFeeds []database.Feed) []Feed {
|
|||||||
}
|
}
|
||||||
return feeds
|
return feeds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func databaseFeedFollowToFeedFollow(dbFeedFollow database.FeedFollow) FeedFollow {
|
||||||
|
return FeedFollow{
|
||||||
|
ID: dbFeedFollow.ID,
|
||||||
|
CreatedAt: dbFeedFollow.CreatedAt,
|
||||||
|
UpdatedAt: dbFeedFollow.UpdatedAt,
|
||||||
|
UserID: dbFeedFollow.UserID,
|
||||||
|
FeedID: dbFeedFollow.FeedID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func databaseFolllowFeedsToFollowFeeds(dbFeedFollows []database.FeedFollow) []FeedFollow {
|
||||||
|
feeds := make([]FeedFollow, len(dbFeedFollows))
|
||||||
|
for i, dbFeed := range dbFeedFollows {
|
||||||
|
feeds[i] = databaseFeedFollowToFeedFollow(dbFeed)
|
||||||
|
}
|
||||||
|
return feeds
|
||||||
|
}
|
||||||
|
10
sql/queries/feed_follows.sql
Normal file
10
sql/queries/feed_follows.sql
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
-- name: CreateFeedFollow :one
|
||||||
|
INSERT INTO feed_follows (created_at, updated_at, user_id, feed_id)
|
||||||
|
VALUES (?, ?, ?, ?)
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: GetFeedFollowsByUser :many
|
||||||
|
SELECT * FROM feed_follows WHERE user_id = ?;
|
||||||
|
|
||||||
|
-- name: DeleteFeedFollow :exec
|
||||||
|
DELETE FROM feed_follows WHERE id = ? AND user_id = ?;
|
12
sql/schema/004_feed_follows.sql
Normal file
12
sql/schema/004_feed_follows.sql
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
-- +goose Up
|
||||||
|
CREATE TABLE feed_follows (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created_at DATE NOT NULL,
|
||||||
|
updated_at DATE NOT NULL,
|
||||||
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
feed_id INTEGER NOT NULL REFERENCES feeds(id) ON DELETE CASCADE,
|
||||||
|
UNIQUE(user_id, feed_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
DROP TABLE feed_follows;
|
Loading…
Reference in New Issue
Block a user