Skip to content
This repository has been archived by the owner on Nov 3, 2024. It is now read-only.

Commit

Permalink
Merge pull request #71 from yoshihiro-shu/update/api/get-articles
Browse files Browse the repository at this point in the history
add: get articles by tag
  • Loading branch information
yoshihiro-shu authored Oct 23, 2023
2 parents 4fc906c + 3530eba commit 2b632f0
Show file tree
Hide file tree
Showing 18 changed files with 273 additions and 73 deletions.
27 changes: 27 additions & 0 deletions backend/application/usecase/articles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package usecase

import (
"github.com/yoshihiro-shu/draft-backend/backend/domain/model"
"github.com/yoshihiro-shu/draft-backend/backend/domain/repository"
)

type ArticlesUseCase interface {
GetArticlesByCategory(articles *[]model.Article, slug string) error
GetArticlesByTag(articles *[]model.Article, slug string) error
}

type articlesUseCase struct {
articleRepo repository.ArticleRepository
}

func NewArticlesUseCase(articleRepo repository.ArticleRepository) ArticlesUseCase {
return &articlesUseCase{articleRepo: articleRepo}
}

func (au *articlesUseCase) GetArticlesByCategory(articles *[]model.Article, slug string) error {
return au.articleRepo.GetArticlesByCategory(articles, slug)
}

func (au *articlesUseCase) GetArticlesByTag(articles *[]model.Article, slug string) error {
return au.articleRepo.GetArticlesByTag(articles, slug)
}
24 changes: 12 additions & 12 deletions backend/domain/model/article.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ import (
)

type Article struct {
Id int `gorm:"primaryKey;" json:"id"`
UserId int `json:"userId"`
ThumbnailUrl string `json:"thumbnailUrl"`
Title string `json:"title"`
Content string `json:"content"`
Status int `json:"status"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
CategoryId int `json:"categoryId"`
User *User `gorm:"foreignKey:UserId;" json:"user"`
Category *Category `gorm:"foreignKey:CategoryId;" json:"category"`
Tags []Tag `gorm:"many2many:article_tags;" json:"tags"`
Id int `gorm:"primaryKey;" json:"id,omitempty"`
UserId int `json:"userId,omitempty"`
ThumbnailUrl string `json:"thumbnailUrl,omitempty"`
Title string `json:"title,omitempty"`
Content string `json:"content,omitempty"`
Status int `json:"status,omitempty"`
CreatedAt time.Time `json:"createdAt,omitempty"`
UpdatedAt time.Time `json:"updatedAt,omitempty"`
CategoryId int `json:"categoryId,omitempty"`
User *User `gorm:"foreignKey:UserId;" json:"user,omitempty"`
Category *Category `gorm:"foreignKey:CategoryId;" json:"category,omitempty"`
Tags []Tag `gorm:"many2many:article_tags;" json:"tags,omitempty"`
}

func NewArticle(Id int) *Article {
Expand Down
11 changes: 5 additions & 6 deletions backend/domain/model/category.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ package model
import "time"

type Category struct {
Id int `gorm:"primaryKey;" json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
Description string `json:"description"`
ParentId int `json:"parentId"`
CreatedAt time.Time `json:"createdAt"`
Id int `gorm:"primaryKey;" json:"id,omitempty"`
Name string `json:"name,omitempty"`
Slug string `json:"slug,omitempty"`
Description string `json:"description,omitempty"`
CreatedAt time.Time `json:"createdAt,omitempty"`
}
14 changes: 7 additions & 7 deletions backend/domain/model/refresh_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import (
)

type RefreshToken struct {
Id int `json:"id"`
UserId int `json:"user_id"`
JwtId string `json:"jwt_id"`
ExpiredAt time.Time `json:"expired_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
User *User `gorm:"foreignKey:user_id;" json:"user"`
Id int `json:"id,omitempty"`
UserId int `json:"user_id,omitempty"`
JwtId string `json:"jwt_id,omitempty"`
ExpiredAt time.Time `json:"expired_at,omitempty"`
CreatedAt time.Time `json:"created_at,omitempty"`
UpdatedAt time.Time `json:"updated_at,omitempty"`
User *User `gorm:"foreignKey:user_id;" json:"user,omitempty"`
}
10 changes: 5 additions & 5 deletions backend/domain/model/tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package model
import "time"

type Tag struct {
Id int `gorm:"primaryKey;" json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
Description string `json:"description"`
CreatedAt time.Time `json:"createdAt"`
Id int `gorm:"primaryKey;" json:"id,omitempty"`
Name string `json:"name,omitempty"`
Slug string `json:"slug,omitempty"`
Description string `json:"description,omitempty"`
CreatedAt time.Time `json:"createdAt,omitempty"`
}

type ArticleTags struct {
Expand Down
10 changes: 5 additions & 5 deletions backend/domain/model/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package model
import "time"

type User struct {
Id int `gorm:"primaryKey;" json:"id"`
Name string `json:"name"`
Password string `json:"password"`
Email string `json:"email"`
CreatedAt time.Time `json:"createdAt"`
Id int `gorm:"primaryKey;" json:"id,omitempty"`
Name string `json:"name,omitempty"`
Password string `json:"password,omitempty"`
Email string `json:"email,omitempty"`
CreatedAt time.Time `json:"createdAt,omitempty"`
}

func NewUser(name, password, email string) *User {
Expand Down
2 changes: 2 additions & 0 deletions backend/domain/repository/article.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ type ArticleRepository interface {
Create(article *model.Article) (*model.Article, error)
FindByID(article *model.Article) error
GetArticles(articles *[]model.Article, limit, offset int) error
GetArticlesByCategory(articles *[]model.Article, slug string) error
GetArticlesByTag(articles *[]model.Article, slug string) error
GetPager(article *model.Article) (int, error)
Update(article *model.Article) (*model.Article, error)
Delete(article *model.Article) error
Expand Down
26 changes: 26 additions & 0 deletions backend/infrastructure/persistence/article.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,32 @@ func (ap *articlePersistence) GetPager(article *model.Article) (int, error) {
return int(count), nil
}

func (ap *articlePersistence) GetArticlesByCategory(articles *[]model.Article, slug string) error {
return ap.Reprica().
// Preload("User").
Preload("Category").
Preload("Tags").
Joins("LEFT JOIN categories AS category ON articles.category_id = category.id").
Where("category.slug = ?", slug).
Find(&articles).Error
}

// GetArticlesByTag retrieves articles based on a given tag slug.
//
// articles: a pointer to a slice of model.Article to store the retrieved articles.
// slug: the slug of the tag to filter the articles by.
// error: an error indicating if there was any issue retrieving the articles.
func (ap *articlePersistence) GetArticlesByTag(articles *[]model.Article, slug string) error {
return ap.Reprica().
// Preload("User").
Preload("Category").
Preload("Tags").
Joins("JOIN article_tags ON articles.id = article_tags.article_id").
Joins("JOIN tags ON tags.id = article_tags.tag_id").
Where("tags.slug = ?", slug).
Find(&articles).Error
}

func (ap *articlePersistence) Update(article *model.Article) (*model.Article, error) {
return &model.Article{}, nil
}
Expand Down
70 changes: 50 additions & 20 deletions backend/interfaces/api/handler/article.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,21 @@ import (

"github.com/gorilla/mux"
"github.com/yoshihiro-shu/draft-backend/backend/application/usecase"
"github.com/yoshihiro-shu/draft-backend/backend/domain/model"
"github.com/yoshihiro-shu/draft-backend/backend/interfaces/api/request"
"gorm.io/gorm"
)

type ArticleHandler interface {
Post(w http.ResponseWriter, r *http.Request) error
Get(w http.ResponseWriter, r *http.Request) error
Put(w http.ResponseWriter, r *http.Request) error
Delete(w http.ResponseWriter, r *http.Request) error
GetArticlesByCategory(w http.ResponseWriter, r *http.Request) error
GetArticlesByTag(w http.ResponseWriter, r *http.Request) error
}

type articleHandler struct {
articleUseCase usecase.ArticleUseCase
C *request.Context
}

func NewArticleHandler(articleUseCase usecase.ArticleUseCase, c *request.Context) ArticleHandler {
return &articleHandler{
articleUseCase: articleUseCase,
C: c,
}
}

func (ah *articleHandler) Post(w http.ResponseWriter, r *http.Request) error {
return nil
articleUseCase usecase.ArticleUseCase
articlesUseCase usecase.ArticlesUseCase
C *request.Context
}

func (ah *articleHandler) Get(w http.ResponseWriter, r *http.Request) error {
Expand All @@ -52,10 +42,50 @@ func (ah *articleHandler) Get(w http.ResponseWriter, r *http.Request) error {
return ah.C.JSON(w, http.StatusOK, article)
}

func (ah *articleHandler) Put(w http.ResponseWriter, r *http.Request) error {
return nil
type responseGetArticlesByCategory struct {
Articles []model.Article `json:"articles"`
}

func (ah *articleHandler) GetArticlesByCategory(w http.ResponseWriter, r *http.Request) error {
var res responseGetArticlesByCategory
vars := mux.Vars(r)
slug := vars["slug"]

err := ah.articlesUseCase.GetArticlesByCategory(&res.Articles, slug)
if err != nil {
if err == gorm.ErrRecordNotFound {
ah.C.Logger.Warn("err no articles at latest Articles Handler")
return ah.C.JSON(w, http.StatusNotFound, err)
}
}

return ah.C.JSON(w, http.StatusOK, res)
}

type responseGetArticlesByTag struct {
Articles []model.Article `json:"articles"`
}

func (ah *articleHandler) GetArticlesByTag(w http.ResponseWriter, r *http.Request) error {
var res responseGetArticlesByTag
vars := mux.Vars(r)
slug := vars["slug"]

err := ah.articlesUseCase.GetArticlesByTag(&res.Articles, slug)
if err != nil {
if err == gorm.ErrRecordNotFound {
ah.C.Logger.Warn("err no articles at latest Articles Handler")
return ah.C.JSON(w, http.StatusNotFound, err)
}
}

return ah.C.JSON(w, http.StatusOK, res)
}

func (ah *articleHandler) Delete(w http.ResponseWriter, r *http.Request) error {
return nil
func NewArticleHandler(articleUseCase usecase.ArticleUseCase, articlesUseCase usecase.ArticlesUseCase, c *request.Context) ArticleHandler {
return &articleHandler{
articleUseCase: articleUseCase,
articlesUseCase: articlesUseCase,
C: c,
}
}
5 changes: 0 additions & 5 deletions backend/interfaces/api/handler/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@ type TestRedis struct {
func (h indexHandler) Index(w http.ResponseWriter, r *http.Request) error {
return h.JSON(w, http.StatusOK, "HELLO WORLD")
}
func (h indexHandler) AuthIndex(w http.ResponseWriter, r *http.Request) error {
id := h.GetAuthUserID(r.Context())
return h.JSON(w, http.StatusOK, id)
}

func NewIndexHandler(c *request.Context) *indexHandler {
return &indexHandler{c}
}
2 changes: 1 addition & 1 deletion backend/interfaces/api/handler/lastest_articles.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (h latestArticlesHandler) Get(w http.ResponseWriter, r *http.Request) error
h.logger.Warn("err no articles at latest Articles Handler")
return h.JSON(w, http.StatusNotFound, err)
}
h.logger.Warn("failed at get articles at latest articles.", zap.Error(err))
h.logger.Error("failed at get articles at latest articles.", zap.Error(err))
return h.Error(w, http.StatusInternalServerError, err)
}

Expand Down
2 changes: 2 additions & 0 deletions backend/interfaces/api/user_api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ func Apply(r router.Router, conf config.Configs, logger logger.Logger, db model.
)
article := r.Group("/articles")
article.GET("/{id:[0-9]+}", articleHandler.Get)
article.GET("/category/{slug}", articleHandler.GetArticlesByCategory)
article.GET("/tag/{slug}", articleHandler.GetArticlesByTag)
}
// {
// a := r.Group("/auth")
Expand Down
3 changes: 2 additions & 1 deletion backend/registory/article_registory.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ import (
func NewArticleRegistory(ctx *request.Context, master, reprica func() *gorm.DB) handler.ArticleHandler {
articleRepository := persistence.NewArticlePersistence(master, reprica)
articleUseCase := usecase.NewArticleUseCase(articleRepository)
return handler.NewArticleHandler(articleUseCase, ctx)
articlesUseCase := usecase.NewArticlesUseCase(articleRepository)
return handler.NewArticleHandler(articleUseCase, articlesUseCase, ctx)
}
42 changes: 40 additions & 2 deletions batch/qiita/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use tokio; // tokioは非同期ランタイムです
use tokio_postgres::{NoTls};

mod qiita_response;
mod tag_map;
mod tag_category_map;

#[derive(Debug)]
struct Tag {
Expand Down Expand Up @@ -81,15 +83,51 @@ async fn main() -> Result<(), Box<dyn Error>> {
});
};

// Insert New Tags
let tag_map = tag_map::create_map();
for r in &res {
for t in &r.tags {
// insert tag if not exists
let check = db_client.query("SELECT * FROM tags WHERE name = $1", &[&t.name]).await?;
if check.len() == 0 {
let slug = tag_map.get(&t.name.as_str());
let mut tag_id: i32 = 0;
if slug.is_none() {
let inserted_tag = db_client.query("INSERT INTO tags (name, slug) VALUES ($1, $2) RETURNING id", &[&t.name, &t.name]).await?;
tag_id = inserted_tag[0].get("id");
} else {
let inserted_tag = db_client.query("INSERT INTO tags (name, slug) VALUES ($1, $2) RETURNING id", &[&t.name, &slug]).await?;
tag_id = inserted_tag[0].get("id");
}
tags.push(Tag{
id: tag_id,
name: t.name.clone(),
});
println!("inserted tag: {}", t.name);
}
}
}

// Insert articles from Qiita
let tag_category_map = tag_category_map::create_map();
for r in res {
let check = db_client.query("SELECT * FROM articles WHERE title = $1", &[&r.title]).await?;
if check.len() != 0 {
println!("already exists!");
continue;
}

let inserted_data = db_client.query("INSERT INTO articles (user_id, thumbnail_url, title, content, status) VALUES ($1, $2, $3, $4, $5) RETURNING id", &[&1, &"",&r.title, &r.body, &2]).await?;
let mut category_id : i32 = Default::default();
for t in &r.tags {
if !tag_category_map.get(&t.name.as_str()).is_none() {
println!("{}: {}", t.name, tag_category_map.get(&t.name.as_str()).unwrap());
let categpory_name = tag_category_map.get(&t.name.as_str()).unwrap().to_string();
let query_get_cagterogry = db_client.query("SELECT id FROM categories WHERE name = $1", &[&categpory_name]).await?;
category_id = query_get_cagterogry[0].get("id");
}
}

let inserted_data = db_client.query("INSERT INTO articles (user_id, thumbnail_url, title, content, status, category_id) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id", &[&1, &"",&r.title, &r.body, &2, &category_id]).await?;
let inserted_id: i32 = inserted_data[0].get("id");
for t in r.tags {
// insert tag if not exists
Expand All @@ -102,14 +140,14 @@ async fn main() -> Result<(), Box<dyn Error>> {
name: t.name.clone(),
})
}

// insert article_tags
for tt in &tags {
if t.name == tt.name {
db_client.execute("INSERT INTO article_tags (article_id, tag_id) VALUES ($1, $2)", &[&inserted_id, &tt.id]).await?;
}
}
}
println!("inserted article: {}", r.title);
}

Ok(())
Expand Down
18 changes: 18 additions & 0 deletions batch/qiita/src/tag_category_map.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use std::collections::HashMap;

pub fn create_map() -> HashMap<&'static str, &'static str> {
let mut tag_category: HashMap<&str, &str> = HashMap::new();

tag_category.insert("インフラ", "Infrastructure");
tag_category.insert("アジャイル", "Agile");
tag_category.insert("ビジネス", "Bussiness");
tag_category.insert("マーケティング", "Marketing");
tag_category.insert("kubernetes", "Infrastructure");
tag_category.insert("Docker", "Infrastructure");
tag_category.insert("要件定義", "System Design");
tag_category.insert("ワイヤーフレーム", "System Design");
tag_category.insert("googlecloud", "Infrastructure");
tag_category.insert("Nuxt", "Frontend");

tag_category
}
Loading

0 comments on commit 2b632f0

Please sign in to comment.