mirror of
https://github.com/ForFarmTeam/ForFarm.git
synced 2025-12-19 22:14:08 +01:00
feat: add knowledgeHub api
This commit is contained in:
parent
b336d94277
commit
38f01d0fbe
@ -26,6 +26,7 @@ type api struct {
|
|||||||
cropRepo domain.CroplandRepository
|
cropRepo domain.CroplandRepository
|
||||||
farmRepo domain.FarmRepository
|
farmRepo domain.FarmRepository
|
||||||
plantRepo domain.PlantRepository
|
plantRepo domain.PlantRepository
|
||||||
|
knowledgeHubRepo domain.KnowledgeHubRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAPI(ctx context.Context, logger *slog.Logger, pool *pgxpool.Pool) *api {
|
func NewAPI(ctx context.Context, logger *slog.Logger, pool *pgxpool.Pool) *api {
|
||||||
@ -36,6 +37,7 @@ func NewAPI(ctx context.Context, logger *slog.Logger, pool *pgxpool.Pool) *api {
|
|||||||
croplandRepository := repository.NewPostgresCropland(pool)
|
croplandRepository := repository.NewPostgresCropland(pool)
|
||||||
farmRepository := repository.NewPostgresFarm(pool)
|
farmRepository := repository.NewPostgresFarm(pool)
|
||||||
plantRepository := repository.NewPostgresPlant(pool)
|
plantRepository := repository.NewPostgresPlant(pool)
|
||||||
|
knowledgeHubRepository := repository.NewPostgresKnowledgeHub(pool)
|
||||||
|
|
||||||
return &api{
|
return &api{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -45,6 +47,7 @@ func NewAPI(ctx context.Context, logger *slog.Logger, pool *pgxpool.Pool) *api {
|
|||||||
cropRepo: croplandRepository,
|
cropRepo: croplandRepository,
|
||||||
farmRepo: farmRepository,
|
farmRepo: farmRepository,
|
||||||
plantRepo: plantRepository,
|
plantRepo: plantRepository,
|
||||||
|
knowledgeHubRepo: knowledgeHubRepository,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,6 +80,7 @@ func (a *api) Routes() *chi.Mux {
|
|||||||
a.registerCropRoutes(r, api)
|
a.registerCropRoutes(r, api)
|
||||||
a.registerPlantRoutes(r, api)
|
a.registerPlantRoutes(r, api)
|
||||||
a.registerOauthRoutes(r, api)
|
a.registerOauthRoutes(r, api)
|
||||||
|
a.registerKnowledgeHubRoutes(r, api)
|
||||||
})
|
})
|
||||||
|
|
||||||
router.Group(func(r chi.Router) {
|
router.Group(func(r chi.Router) {
|
||||||
|
|||||||
253
backend/internal/api/knowledgeHub.go
Normal file
253
backend/internal/api/knowledgeHub.go
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/danielgtaylor/huma/v2"
|
||||||
|
"github.com/forfarm/backend/internal/domain"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/gofrs/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *api) registerKnowledgeHubRoutes(_ chi.Router, api huma.API) {
|
||||||
|
tags := []string{"knowledge-hub"}
|
||||||
|
|
||||||
|
prefix := "/knowledge-hub"
|
||||||
|
|
||||||
|
huma.Register(api, huma.Operation{
|
||||||
|
OperationID: "getAllKnowledgeArticles",
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: prefix,
|
||||||
|
Tags: tags,
|
||||||
|
}, a.getAllKnowledgeArticlesHandler)
|
||||||
|
|
||||||
|
huma.Register(api, huma.Operation{
|
||||||
|
OperationID: "getKnowledgeArticleByID",
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: prefix + "/{uuid}",
|
||||||
|
Tags: tags,
|
||||||
|
}, a.getKnowledgeArticleByIDHandler)
|
||||||
|
|
||||||
|
huma.Register(api, huma.Operation{
|
||||||
|
OperationID: "getKnowledgeArticlesByCategory",
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: prefix + "/category/{category}",
|
||||||
|
Tags: tags,
|
||||||
|
}, a.getKnowledgeArticlesByCategoryHandler)
|
||||||
|
|
||||||
|
huma.Register(api, huma.Operation{
|
||||||
|
OperationID: "createOrUpdateKnowledgeArticle",
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: prefix,
|
||||||
|
Tags: tags,
|
||||||
|
}, a.createOrUpdateKnowledgeArticleHandler)
|
||||||
|
|
||||||
|
huma.Register(api, huma.Operation{
|
||||||
|
OperationID: "getArticleTableOfContents",
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: prefix + "/{uuid}/toc",
|
||||||
|
Tags: tags,
|
||||||
|
}, a.getArticleTableOfContentsHandler)
|
||||||
|
|
||||||
|
huma.Register(api, huma.Operation{
|
||||||
|
OperationID: "getArticleRelatedArticles",
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: prefix + "/{uuid}/related",
|
||||||
|
Tags: tags,
|
||||||
|
}, a.getArticleRelatedArticlesHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetKnowledgeArticlesOutput struct {
|
||||||
|
Body struct {
|
||||||
|
Articles []domain.KnowledgeArticle `json:"articles"`
|
||||||
|
} `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetKnowledgeArticleByIDOutput struct {
|
||||||
|
Body struct {
|
||||||
|
Article domain.KnowledgeArticle `json:"article"`
|
||||||
|
} `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateOrUpdateKnowledgeArticleInput struct {
|
||||||
|
Body struct {
|
||||||
|
UUID string `json:"uuid,omitempty"` // Optional for create, required for update
|
||||||
|
Title string `json:"title"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
Author string `json:"author"`
|
||||||
|
PublishDate time.Time `json:"publish_date"`
|
||||||
|
ReadTime string `json:"read_time"`
|
||||||
|
Categories []string `json:"categories"`
|
||||||
|
} `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateOrUpdateKnowledgeArticleOutput struct {
|
||||||
|
Body struct {
|
||||||
|
Article domain.KnowledgeArticle `json:"article"`
|
||||||
|
} `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetTableOfContentsOutput struct {
|
||||||
|
Body struct {
|
||||||
|
TableOfContents []domain.TableOfContent `json:"table_of_contents"`
|
||||||
|
} `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetRelatedArticlesOutput struct {
|
||||||
|
Body struct {
|
||||||
|
RelatedArticles []domain.RelatedArticle `json:"related_articles"`
|
||||||
|
} `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *api) getAllKnowledgeArticlesHandler(ctx context.Context, input *struct{}) (*GetKnowledgeArticlesOutput, error) {
|
||||||
|
resp := &GetKnowledgeArticlesOutput{}
|
||||||
|
|
||||||
|
articles, err := a.knowledgeHubRepo.GetAllArticles(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Body.Articles = articles
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *api) getKnowledgeArticleByIDHandler(ctx context.Context, input *struct {
|
||||||
|
UUID string `path:"uuid" example:"550e8400-e29b-41d4-a716-446655440000"`
|
||||||
|
}) (*GetKnowledgeArticleByIDOutput, error) {
|
||||||
|
resp := &GetKnowledgeArticleByIDOutput{}
|
||||||
|
|
||||||
|
if input.UUID == "" {
|
||||||
|
return nil, huma.Error400BadRequest("UUID parameter is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := uuid.FromString(input.UUID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, huma.Error400BadRequest("invalid UUID format")
|
||||||
|
}
|
||||||
|
|
||||||
|
article, err := a.knowledgeHubRepo.GetArticleByID(ctx, input.UUID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, domain.ErrNotFound) {
|
||||||
|
return nil, huma.Error404NotFound("article not found")
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Body.Article = article
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *api) getKnowledgeArticlesByCategoryHandler(ctx context.Context, input *struct {
|
||||||
|
Category string `path:"category" example:"Sustainability"`
|
||||||
|
}) (*GetKnowledgeArticlesOutput, error) {
|
||||||
|
resp := &GetKnowledgeArticlesOutput{}
|
||||||
|
|
||||||
|
if input.Category == "" {
|
||||||
|
return nil, huma.Error400BadRequest("category parameter is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
articles, err := a.knowledgeHubRepo.GetArticlesByCategory(ctx, input.Category)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Body.Articles = articles
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *api) createOrUpdateKnowledgeArticleHandler(ctx context.Context, input *CreateOrUpdateKnowledgeArticleInput) (*CreateOrUpdateKnowledgeArticleOutput, error) {
|
||||||
|
resp := &CreateOrUpdateKnowledgeArticleOutput{}
|
||||||
|
|
||||||
|
if input.Body.Title == "" {
|
||||||
|
return nil, huma.Error400BadRequest("title is required")
|
||||||
|
}
|
||||||
|
if input.Body.Content == "" {
|
||||||
|
return nil, huma.Error400BadRequest("content is required")
|
||||||
|
}
|
||||||
|
if input.Body.Author == "" {
|
||||||
|
return nil, huma.Error400BadRequest("author is required")
|
||||||
|
}
|
||||||
|
if len(input.Body.Categories) == 0 {
|
||||||
|
return nil, huma.Error400BadRequest("at least one category is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.Body.UUID != "" {
|
||||||
|
_, err := uuid.FromString(input.Body.UUID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, huma.Error400BadRequest("invalid UUID format")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
article := &domain.KnowledgeArticle{
|
||||||
|
UUID: input.Body.UUID,
|
||||||
|
Title: input.Body.Title,
|
||||||
|
Content: input.Body.Content,
|
||||||
|
Author: input.Body.Author,
|
||||||
|
PublishDate: input.Body.PublishDate,
|
||||||
|
ReadTime: input.Body.ReadTime,
|
||||||
|
Categories: input.Body.Categories,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := a.knowledgeHubRepo.CreateOrUpdateArticle(ctx, article)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Body.Article = *article
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *api) getArticleTableOfContentsHandler(ctx context.Context, input *struct {
|
||||||
|
UUID string `path:"uuid" example:"550e8400-e29b-41d4-a716-446655440000"`
|
||||||
|
}) (*GetTableOfContentsOutput, error) {
|
||||||
|
resp := &GetTableOfContentsOutput{}
|
||||||
|
|
||||||
|
if input.UUID == "" {
|
||||||
|
return nil, huma.Error400BadRequest("UUID parameter is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := uuid.FromString(input.UUID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, huma.Error400BadRequest("invalid UUID format")
|
||||||
|
}
|
||||||
|
|
||||||
|
toc, err := a.knowledgeHubRepo.GetTableOfContents(ctx, input.UUID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, domain.ErrNotFound) {
|
||||||
|
return nil, huma.Error404NotFound("article not found")
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Body.TableOfContents = toc
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *api) getArticleRelatedArticlesHandler(ctx context.Context, input *struct {
|
||||||
|
UUID string `path:"uuid" example:"550e8400-e29b-41d4-a716-446655440000"`
|
||||||
|
}) (*GetRelatedArticlesOutput, error) {
|
||||||
|
resp := &GetRelatedArticlesOutput{}
|
||||||
|
|
||||||
|
if input.UUID == "" {
|
||||||
|
return nil, huma.Error400BadRequest("UUID parameter is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := uuid.FromString(input.UUID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, huma.Error400BadRequest("invalid UUID format")
|
||||||
|
}
|
||||||
|
|
||||||
|
related, err := a.knowledgeHubRepo.GetRelatedArticles(ctx, input.UUID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, domain.ErrNotFound) {
|
||||||
|
return nil, huma.Error404NotFound("article not found")
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Body.RelatedArticles = related
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user