Skip to content

Commit

Permalink
Merge pull request #6 from itishrishikesh/auth
Browse files Browse the repository at this point in the history
Authentication middleware added. Added feed entity's schema, and handler.
  • Loading branch information
itishrishikesh authored Dec 10, 2023
2 parents 8ff51ed + 2d22a3a commit 8030851
Show file tree
Hide file tree
Showing 13 changed files with 340 additions and 15 deletions.
28 changes: 28 additions & 0 deletions auth_middleware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package main

import (
"fmt"
"net/http"

"github.com/itishrishikesh/gofeed/auth"
"github.com/itishrishikesh/gofeed/constants"
"github.com/itishrishikesh/gofeed/internal/database"
)

type authHeader func(http.ResponseWriter, *http.Request, database.User)

func (config *apiConfig) authMiddleware(handler authHeader) http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
apiKey, err := auth.GetAPIKey(request.Header)
if err != nil {
respondWithError(writer, constants.HTTP_FORBIDDEN, fmt.Sprintf("E#1OV41H - Authentication Error: %v", err))
return
}
user, err := config.DB.GetUserByAPIKey(request.Context(), apiKey)
if err != nil {
respondWithError(writer, constants.HTTP_BAD_REQUEST, fmt.Sprintf("E#1OV44M - Couldn't get user: %v", err))
return
}
handler(writer, request, user)
}
}
2 changes: 2 additions & 0 deletions constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@ package constants
const CONTENT_TYPE string = "Content-Type"
const APPLICATION_JSON string = "application/json"
const HTTP_SUCCESS int = 200
const HTTP_CREATED int = 200
const HTTP_ERROR int = 500
const HTTP_BAD_REQUEST int = 400
const HTTP_FORBIDDEN int = 400
77 changes: 77 additions & 0 deletions feed_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package main

import (
"encoding/json"
"fmt"
"log"
"net/http"
"time"

"github.com/google/uuid"
"github.com/itishrishikesh/gofeed/constants"
"github.com/itishrishikesh/gofeed/internal/database"
)

func (apiCfg *apiConfig) createFeedHandler(writer http.ResponseWriter, request *http.Request, user database.User) {
type parameters struct {
Name string `json:"name"`
URL string `json:"url"`
}
params := parameters{}
decoder := json.NewDecoder(request.Body)
err := decoder.Decode(&params)
if err != nil {
respondWithError(writer, constants.HTTP_BAD_REQUEST, "Error parsing JSON")
log.Println("D#1OV4W5 - User passed incorrect JSON")
return
}
feed, err := apiCfg.DB.CreateFeed(request.Context(), database.CreateFeedParams{
ID: uuid.New(),
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
Name: params.Name,
Url: params.URL,
UserID: user.ID,
})
if err != nil {
respondWithError(writer, constants.HTTP_BAD_REQUEST, fmt.Sprintf("E#1OV5DR - Couldn't create feed %v", err))
return
}

respondWithJSON(writer, constants.HTTP_CREATED, databaseFeedToFeed(feed))
}

func (config *apiConfig) getFeedsHandler(writer http.ResponseWriter, request *http.Request) {
feeds, err := config.DB.GetFeed(request.Context())
if err != nil {
respondWithError(writer, constants.HTTP_BAD_REQUEST, fmt.Sprintf("E#1OV63D - Couldn't get feed %v", err))
return
}
respondWithJSON(writer, constants.HTTP_CREATED, databaseFeedToFeeds(feeds))
}

func (config *apiConfig) createFeedFollowHandler(writer http.ResponseWriter, request *http.Request, user database.User) {
type parameters struct {
FeedID uuid.UUID `json:"feed_id"`
}
params := parameters{}
decoder := json.NewDecoder(request.Body)
err := decoder.Decode(&params)
if err != nil {
respondWithError(writer, constants.HTTP_BAD_REQUEST, "Error parsing JSON")
log.Println("D#1OV7DL - User passed incorrect JSON")
return
}
feedFollow, err := config.DB.CreateFeedFollow(request.Context(), database.CreateFeedFollowParams{
ID: uuid.New(),
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
UserID: user.ID,
FeedID: params.FeedID,
})
if err != nil {
respondWithError(writer, constants.HTTP_BAD_REQUEST, fmt.Sprintf("E#1OV7DV - Couldn't create feed follow %v", err))
return
}
respondWithJSON(writer, constants.HTTP_CREATED, databaseFeedFollowToFeedFollow(feedFollow))
}
83 changes: 83 additions & 0 deletions internal/database/feeds.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 46 additions & 0 deletions internal/database/feeds_follow.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions internal/database/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ func main() {
v1Router.Get("/healthz", healthCheckHandler)
v1Router.Get("/err", errorHandler)
v1Router.Post("/users", apiCfg.createUserHandler)
v1Router.Get("/users", apiCfg.getUserHandler)
v1Router.Get("/users", apiCfg.authMiddleware(apiCfg.getUserHandler))
v1Router.Post("/feeds", apiCfg.authMiddleware(apiCfg.createFeedHandler))
v1Router.Get("/feeds", apiCfg.getFeedsHandler)
v1Router.Post("/feedfollow", apiCfg.authMiddleware(apiCfg.createFeedFollowHandler))
router.Mount("/v1", v1Router)

server := &http.Server{
Expand Down
46 changes: 46 additions & 0 deletions models.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,49 @@ func databaseUserToUser(dbUser database.User) User {
ApiKey: dbUser.ApiKey,
}
}

type Feed struct {
ID uuid.UUID `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Name string `json:"name"`
Url string `json:"url"`
UserID uuid.UUID `json:"user_id"`
}

func databaseFeedToFeed(dbFeed database.Feed) Feed {
return Feed{
ID: dbFeed.ID,
CreatedAt: dbFeed.CreatedAt,
UpdatedAt: dbFeed.UpdatedAt,
Name: dbFeed.Name,
Url: dbFeed.Url,
UserID: dbFeed.UserID,
}
}

func databaseFeedToFeeds(dbFeeds []database.Feed) []Feed {
feeds := []Feed{}
for _, dbFeed := range dbFeeds {
feeds = append(feeds, databaseFeedToFeed(dbFeed))
}
return feeds
}

type FeedFollow struct {
ID uuid.UUID `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
UserID uuid.UUID `json:"user_id"`
FeedID uuid.UUID `json:"feed_id"`
}

func databaseFeedFollowToFeedFollow(dbFeedFollow database.FeedFollow) FeedFollow {
return FeedFollow{
ID: dbFeedFollow.ID,
CreatedAt: dbFeedFollow.CreatedAt,
UpdatedAt: dbFeedFollow.UpdatedAt,
UserID: dbFeedFollow.UserID,
FeedID: dbFeedFollow.FeedID,
}
}
7 changes: 7 additions & 0 deletions sql/queries/feeds.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-- name: CreateFeed :one
INSERT INTO feeds (id, created_at, updated_at, name, url, user_id)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING *;

-- name: GetFeed :many
SELECT * from feeds;
4 changes: 4 additions & 0 deletions sql/queries/feeds_follow.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- name: CreateFeedFollow :one
INSERT INTO feed_follows (id, created_at, updated_at, user_id, feed_id)
VALUES ($1, $2, $3, $4, $5)
RETURNING *;
12 changes: 12 additions & 0 deletions sql/schema/003_feeds.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-- +goose Up
CREATE TABLE feeds (
id UUID PRIMARY KEY,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL,
name TEXT NOT NULL,
url TEXT UNIQUE NOT NULL,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE
);

-- +goose Down
DROP TABLE feeds;
12 changes: 12 additions & 0 deletions sql/schema/004_feed_follows.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-- +goose Up
CREATE TABLE feed_follows (
id UUID PRIMARY KEY,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
feed_id UUID NOT NULL REFERENCES feeds(id) ON DELETE CASCADE,
UNIQUE(user_id, feed_id)
);

-- +goose Down
DROP TABLE feed_follows;
Loading

0 comments on commit 8030851

Please sign in to comment.