feat: implement wiki and wiki content management system including database schemas, DTOs, and API endpoints
All checks were successful
Build and Release / release (push) Successful in 1m33s
All checks were successful
Build and Release / release (push) Successful in 1m33s
This commit is contained in:
@@ -5,7 +5,6 @@ CREATE TABLE IF NOT EXISTS wikis (
|
|||||||
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||||||
title TEXT,
|
title TEXT,
|
||||||
slug TEXT,
|
slug TEXT,
|
||||||
content TEXT,
|
|
||||||
is_deleted BOOLEAN NOT NULL DEFAULT false,
|
is_deleted BOOLEAN NOT NULL DEFAULT false,
|
||||||
created_at TIMESTAMPTZ DEFAULT now(),
|
created_at TIMESTAMPTZ DEFAULT now(),
|
||||||
updated_at TIMESTAMPTZ DEFAULT now()
|
updated_at TIMESTAMPTZ DEFAULT now()
|
||||||
|
|||||||
1
db/migrations/000016_wiki_content.down.sql
Normal file
1
db/migrations/000016_wiki_content.down.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
DROP TABLE IF EXISTS wiki_content;
|
||||||
11
db/migrations/000016_wiki_content.up.sql
Normal file
11
db/migrations/000016_wiki_content.up.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS wiki_content (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuidv7(),
|
||||||
|
wiki_id UUID NOT NULL REFERENCES wikis(id) ON DELETE CASCADE,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
content TEXT,
|
||||||
|
is_deleted BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT now()
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_wiki_content_wiki_id ON wiki_content(wiki_id);
|
||||||
|
CREATE INDEX idx_wiki_content_created_at ON wiki_content(created_at DESC);
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
-- name: CreateWiki :one
|
-- name: CreateWiki :one
|
||||||
INSERT INTO wikis (
|
INSERT INTO wikis (
|
||||||
id, title, slug, content, project_id
|
id, title, slug, project_id
|
||||||
) VALUES (
|
) VALUES (
|
||||||
COALESCE(sqlc.narg('id')::uuid, uuidv7()), $1, $2, $3, $4
|
COALESCE(sqlc.narg('id')::uuid, uuidv7()), $1, $2, $3
|
||||||
)
|
)
|
||||||
RETURNING *;
|
RETURNING *;
|
||||||
|
|
||||||
@@ -16,7 +16,6 @@ UPDATE wikis
|
|||||||
SET
|
SET
|
||||||
title = COALESCE(sqlc.narg('title'), title),
|
title = COALESCE(sqlc.narg('title'), title),
|
||||||
slug = COALESCE(sqlc.narg('slug'), slug),
|
slug = COALESCE(sqlc.narg('slug'), slug),
|
||||||
content = COALESCE(sqlc.narg('content'), content),
|
|
||||||
project_id = COALESCE(sqlc.narg('project_id'), project_id)
|
project_id = COALESCE(sqlc.narg('project_id'), project_id)
|
||||||
WHERE id = sqlc.arg('id') AND is_deleted = false
|
WHERE id = sqlc.arg('id') AND is_deleted = false
|
||||||
RETURNING *;
|
RETURNING *;
|
||||||
@@ -93,3 +92,38 @@ WHERE slug = $1 AND is_deleted = false;
|
|||||||
|
|
||||||
-- name: GetWikisBySlugs :many
|
-- name: GetWikisBySlugs :many
|
||||||
SELECT * FROM wikis WHERE slug = ANY($1::text[]) AND is_deleted = false;
|
SELECT * FROM wikis WHERE slug = ANY($1::text[]) AND is_deleted = false;
|
||||||
|
|
||||||
|
-- name: CreateWikiContent :one
|
||||||
|
INSERT INTO wiki_content (
|
||||||
|
id, wiki_id, title, content
|
||||||
|
) VALUES (
|
||||||
|
COALESCE(sqlc.narg('id')::uuid, uuidv7()), $1, $2, $3
|
||||||
|
)
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: GetWikiContentCount :one
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM wiki_content
|
||||||
|
WHERE wiki_id = $1;
|
||||||
|
|
||||||
|
-- name: GetWikiContentById :one
|
||||||
|
SELECT *
|
||||||
|
FROM wiki_content
|
||||||
|
WHERE id = $1 AND is_deleted = false;
|
||||||
|
|
||||||
|
-- name: GetWikiContentByIDs :many
|
||||||
|
SELECT *
|
||||||
|
FROM wiki_content
|
||||||
|
WHERE id = ANY($1::uuid[]) AND is_deleted = false;
|
||||||
|
|
||||||
|
-- name: GetWikiContentByWikiID :many
|
||||||
|
SELECT id, title, created_at
|
||||||
|
FROM wiki_content
|
||||||
|
WHERE wiki_id = $1 AND is_deleted = false
|
||||||
|
ORDER BY created_at DESC;
|
||||||
|
|
||||||
|
-- name: GetWikiContentByWikiIDs :many
|
||||||
|
SELECT id, wiki_id, title, created_at
|
||||||
|
FROM wiki_content
|
||||||
|
WHERE wiki_id = ANY($1::uuid[]) AND is_deleted = false
|
||||||
|
ORDER BY created_at DESC;
|
||||||
|
|||||||
@@ -102,12 +102,20 @@ CREATE TABLE IF NOT EXISTS wikis (
|
|||||||
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||||||
title TEXT,
|
title TEXT,
|
||||||
slug TEXT UNIQUE,
|
slug TEXT UNIQUE,
|
||||||
content TEXT,
|
|
||||||
is_deleted BOOLEAN NOT NULL DEFAULT false,
|
is_deleted BOOLEAN NOT NULL DEFAULT false,
|
||||||
created_at TIMESTAMPTZ DEFAULT now(),
|
created_at TIMESTAMPTZ DEFAULT now(),
|
||||||
updated_at TIMESTAMPTZ DEFAULT now()
|
updated_at TIMESTAMPTZ DEFAULT now()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS wiki_content (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuidv7(),
|
||||||
|
wiki_id UUID NOT NULL REFERENCES wikis(id) ON DELETE CASCADE,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
content TEXT,
|
||||||
|
is_deleted BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT now()
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS entity_wikis (
|
CREATE TABLE IF NOT EXISTS entity_wikis (
|
||||||
entity_id UUID REFERENCES entities(id) ON DELETE CASCADE,
|
entity_id UUID REFERENCES entities(id) ON DELETE CASCADE,
|
||||||
wiki_id UUID REFERENCES wikis(id) ON DELETE CASCADE,
|
wiki_id UUID REFERENCES wikis(id) ON DELETE CASCADE,
|
||||||
|
|||||||
@@ -136,3 +136,30 @@ func (h *WikiController) SearchWikis(c fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetWikiContentById handles fetching a single wiki content by ID.
|
||||||
|
// @Summary Get wiki content by ID
|
||||||
|
// @Description Get detailed information about a specific wiki content version
|
||||||
|
// @Tags Wikis
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path string true "Wiki Content ID"
|
||||||
|
// @Success 200 {object} response.CommonResponse
|
||||||
|
// @Failure 500 {object} response.CommonResponse
|
||||||
|
// @Router /wikis/content/{id} [get]
|
||||||
|
func (h *WikiController) GetWikiContentById(c fiber.Ctx) error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
id := c.Params("id")
|
||||||
|
res, err := h.service.GetWikiContentByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(err.Code).JSON(response.CommonResponse{
|
||||||
|
Status: false,
|
||||||
|
Message: err.Message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return c.Status(fiber.StatusOK).JSON(response.CommonResponse{
|
||||||
|
Status: true,
|
||||||
|
Data: res,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,27 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type WikiContentSample struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
CreatedAt *time.Time `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
type WikiResponse struct {
|
type WikiResponse struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Title string `json:"title,omitempty"`
|
Title string `json:"title,omitempty"`
|
||||||
Slug string `json:"slug,omitempty"`
|
Slug string `json:"slug,omitempty"`
|
||||||
Content string `json:"content,omitempty"`
|
ContentSample []WikiContentSample `json:"content_sample,omitempty"`
|
||||||
ProjectID string `json:"project_id"`
|
ProjectID string `json:"project_id"`
|
||||||
IsDeleted bool `json:"is_deleted,omitempty"`
|
IsDeleted bool `json:"is_deleted,omitempty"`
|
||||||
CreatedAt *time.Time `json:"created_at,omitempty"`
|
CreatedAt *time.Time `json:"created_at,omitempty"`
|
||||||
UpdatedAt *time.Time `json:"updated_at,omitempty"`
|
UpdatedAt *time.Time `json:"updated_at,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WikiContentResponse struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
WikiID string `json:"wiki_id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
CreatedAt *time.Time `json:"created_at"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -234,8 +234,16 @@ type Wiki struct {
|
|||||||
ProjectID pgtype.UUID `json:"project_id"`
|
ProjectID pgtype.UUID `json:"project_id"`
|
||||||
Title pgtype.Text `json:"title"`
|
Title pgtype.Text `json:"title"`
|
||||||
Slug pgtype.Text `json:"slug"`
|
Slug pgtype.Text `json:"slug"`
|
||||||
Content pgtype.Text `json:"content"`
|
|
||||||
IsDeleted bool `json:"is_deleted"`
|
IsDeleted bool `json:"is_deleted"`
|
||||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WikiContent struct {
|
||||||
|
ID pgtype.UUID `json:"id"`
|
||||||
|
WikiID pgtype.UUID `json:"wiki_id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Content pgtype.Text `json:"content"`
|
||||||
|
IsDeleted bool `json:"is_deleted"`
|
||||||
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -68,17 +68,16 @@ func (q *Queries) CreateEntityWikis(ctx context.Context, arg CreateEntityWikisPa
|
|||||||
|
|
||||||
const createWiki = `-- name: CreateWiki :one
|
const createWiki = `-- name: CreateWiki :one
|
||||||
INSERT INTO wikis (
|
INSERT INTO wikis (
|
||||||
id, title, slug, content, project_id
|
id, title, slug, project_id
|
||||||
) VALUES (
|
) VALUES (
|
||||||
COALESCE($5::uuid, uuidv7()), $1, $2, $3, $4
|
COALESCE($4::uuid, uuidv7()), $1, $2, $3
|
||||||
)
|
)
|
||||||
RETURNING id, project_id, title, slug, content, is_deleted, created_at, updated_at
|
RETURNING id, project_id, title, slug, is_deleted, created_at, updated_at
|
||||||
`
|
`
|
||||||
|
|
||||||
type CreateWikiParams struct {
|
type CreateWikiParams struct {
|
||||||
Title pgtype.Text `json:"title"`
|
Title pgtype.Text `json:"title"`
|
||||||
Slug pgtype.Text `json:"slug"`
|
Slug pgtype.Text `json:"slug"`
|
||||||
Content pgtype.Text `json:"content"`
|
|
||||||
ProjectID pgtype.UUID `json:"project_id"`
|
ProjectID pgtype.UUID `json:"project_id"`
|
||||||
ID pgtype.UUID `json:"id"`
|
ID pgtype.UUID `json:"id"`
|
||||||
}
|
}
|
||||||
@@ -87,7 +86,6 @@ func (q *Queries) CreateWiki(ctx context.Context, arg CreateWikiParams) (Wiki, e
|
|||||||
row := q.db.QueryRow(ctx, createWiki,
|
row := q.db.QueryRow(ctx, createWiki,
|
||||||
arg.Title,
|
arg.Title,
|
||||||
arg.Slug,
|
arg.Slug,
|
||||||
arg.Content,
|
|
||||||
arg.ProjectID,
|
arg.ProjectID,
|
||||||
arg.ID,
|
arg.ID,
|
||||||
)
|
)
|
||||||
@@ -97,7 +95,6 @@ func (q *Queries) CreateWiki(ctx context.Context, arg CreateWikiParams) (Wiki, e
|
|||||||
&i.ProjectID,
|
&i.ProjectID,
|
||||||
&i.Title,
|
&i.Title,
|
||||||
&i.Slug,
|
&i.Slug,
|
||||||
&i.Content,
|
|
||||||
&i.IsDeleted,
|
&i.IsDeleted,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
@@ -105,6 +102,41 @@ func (q *Queries) CreateWiki(ctx context.Context, arg CreateWikiParams) (Wiki, e
|
|||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createWikiContent = `-- name: CreateWikiContent :one
|
||||||
|
INSERT INTO wiki_content (
|
||||||
|
id, wiki_id, title, content
|
||||||
|
) VALUES (
|
||||||
|
COALESCE($4::uuid, uuidv7()), $1, $2, $3
|
||||||
|
)
|
||||||
|
RETURNING id, wiki_id, title, content, is_deleted, created_at
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateWikiContentParams struct {
|
||||||
|
WikiID pgtype.UUID `json:"wiki_id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Content pgtype.Text `json:"content"`
|
||||||
|
ID pgtype.UUID `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateWikiContent(ctx context.Context, arg CreateWikiContentParams) (WikiContent, error) {
|
||||||
|
row := q.db.QueryRow(ctx, createWikiContent,
|
||||||
|
arg.WikiID,
|
||||||
|
arg.Title,
|
||||||
|
arg.Content,
|
||||||
|
arg.ID,
|
||||||
|
)
|
||||||
|
var i WikiContent
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.WikiID,
|
||||||
|
&i.Title,
|
||||||
|
&i.Content,
|
||||||
|
&i.IsDeleted,
|
||||||
|
&i.CreatedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
const deleteEntityWiki = `-- name: DeleteEntityWiki :exec
|
const deleteEntityWiki = `-- name: DeleteEntityWiki :exec
|
||||||
DELETE FROM entity_wikis
|
DELETE FROM entity_wikis
|
||||||
WHERE entity_id = $1 AND wiki_id = $2
|
WHERE entity_id = $1 AND wiki_id = $2
|
||||||
@@ -154,7 +186,7 @@ func (q *Queries) DeleteWikisByIDs(ctx context.Context, dollar_1 []pgtype.UUID)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getWikiById = `-- name: GetWikiById :one
|
const getWikiById = `-- name: GetWikiById :one
|
||||||
SELECT id, project_id, title, slug, content, is_deleted, created_at, updated_at
|
SELECT id, project_id, title, slug, is_deleted, created_at, updated_at
|
||||||
FROM wikis
|
FROM wikis
|
||||||
WHERE id = $1 AND is_deleted = false
|
WHERE id = $1 AND is_deleted = false
|
||||||
`
|
`
|
||||||
@@ -167,7 +199,6 @@ func (q *Queries) GetWikiById(ctx context.Context, id pgtype.UUID) (Wiki, error)
|
|||||||
&i.ProjectID,
|
&i.ProjectID,
|
||||||
&i.Title,
|
&i.Title,
|
||||||
&i.Slug,
|
&i.Slug,
|
||||||
&i.Content,
|
|
||||||
&i.IsDeleted,
|
&i.IsDeleted,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
@@ -176,7 +207,7 @@ func (q *Queries) GetWikiById(ctx context.Context, id pgtype.UUID) (Wiki, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getWikiBySlug = `-- name: GetWikiBySlug :one
|
const getWikiBySlug = `-- name: GetWikiBySlug :one
|
||||||
SELECT id, project_id, title, slug, content, is_deleted, created_at, updated_at
|
SELECT id, project_id, title, slug, is_deleted, created_at, updated_at
|
||||||
FROM wikis
|
FROM wikis
|
||||||
WHERE slug = $1 AND is_deleted = false
|
WHERE slug = $1 AND is_deleted = false
|
||||||
`
|
`
|
||||||
@@ -189,7 +220,6 @@ func (q *Queries) GetWikiBySlug(ctx context.Context, slug pgtype.Text) (Wiki, er
|
|||||||
&i.ProjectID,
|
&i.ProjectID,
|
||||||
&i.Title,
|
&i.Title,
|
||||||
&i.Slug,
|
&i.Slug,
|
||||||
&i.Content,
|
|
||||||
&i.IsDeleted,
|
&i.IsDeleted,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
@@ -197,8 +227,146 @@ func (q *Queries) GetWikiBySlug(ctx context.Context, slug pgtype.Text) (Wiki, er
|
|||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getWikiContentByIDs = `-- name: GetWikiContentByIDs :many
|
||||||
|
SELECT id, wiki_id, title, content, is_deleted, created_at
|
||||||
|
FROM wiki_content
|
||||||
|
WHERE id = ANY($1::uuid[]) AND is_deleted = false
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetWikiContentByIDs(ctx context.Context, dollar_1 []pgtype.UUID) ([]WikiContent, error) {
|
||||||
|
rows, err := q.db.Query(ctx, getWikiContentByIDs, dollar_1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
items := []WikiContent{}
|
||||||
|
for rows.Next() {
|
||||||
|
var i WikiContent
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.WikiID,
|
||||||
|
&i.Title,
|
||||||
|
&i.Content,
|
||||||
|
&i.IsDeleted,
|
||||||
|
&i.CreatedAt,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const getWikiContentById = `-- name: GetWikiContentById :one
|
||||||
|
SELECT id, wiki_id, title, content, is_deleted, created_at
|
||||||
|
FROM wiki_content
|
||||||
|
WHERE id = $1 AND is_deleted = false
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetWikiContentById(ctx context.Context, id pgtype.UUID) (WikiContent, error) {
|
||||||
|
row := q.db.QueryRow(ctx, getWikiContentById, id)
|
||||||
|
var i WikiContent
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.WikiID,
|
||||||
|
&i.Title,
|
||||||
|
&i.Content,
|
||||||
|
&i.IsDeleted,
|
||||||
|
&i.CreatedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getWikiContentByWikiID = `-- name: GetWikiContentByWikiID :many
|
||||||
|
SELECT id, title, created_at
|
||||||
|
FROM wiki_content
|
||||||
|
WHERE wiki_id = $1 AND is_deleted = false
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetWikiContentByWikiIDRow struct {
|
||||||
|
ID pgtype.UUID `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetWikiContentByWikiID(ctx context.Context, wikiID pgtype.UUID) ([]GetWikiContentByWikiIDRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, getWikiContentByWikiID, wikiID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
items := []GetWikiContentByWikiIDRow{}
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetWikiContentByWikiIDRow
|
||||||
|
if err := rows.Scan(&i.ID, &i.Title, &i.CreatedAt); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const getWikiContentByWikiIDs = `-- name: GetWikiContentByWikiIDs :many
|
||||||
|
SELECT id, wiki_id, title, created_at
|
||||||
|
FROM wiki_content
|
||||||
|
WHERE wiki_id = ANY($1::uuid[]) AND is_deleted = false
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetWikiContentByWikiIDsRow struct {
|
||||||
|
ID pgtype.UUID `json:"id"`
|
||||||
|
WikiID pgtype.UUID `json:"wiki_id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetWikiContentByWikiIDs(ctx context.Context, dollar_1 []pgtype.UUID) ([]GetWikiContentByWikiIDsRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, getWikiContentByWikiIDs, dollar_1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
items := []GetWikiContentByWikiIDsRow{}
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetWikiContentByWikiIDsRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.WikiID,
|
||||||
|
&i.Title,
|
||||||
|
&i.CreatedAt,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const getWikiContentCount = `-- name: GetWikiContentCount :one
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM wiki_content
|
||||||
|
WHERE wiki_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetWikiContentCount(ctx context.Context, wikiID pgtype.UUID) (int64, error) {
|
||||||
|
row := q.db.QueryRow(ctx, getWikiContentCount, wikiID)
|
||||||
|
var count int64
|
||||||
|
err := row.Scan(&count)
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
const getWikisByIDs = `-- name: GetWikisByIDs :many
|
const getWikisByIDs = `-- name: GetWikisByIDs :many
|
||||||
SELECT id, project_id, title, slug, content, is_deleted, created_at, updated_at FROM wikis WHERE id = ANY($1::uuid[]) AND is_deleted = false
|
SELECT id, project_id, title, slug, is_deleted, created_at, updated_at FROM wikis WHERE id = ANY($1::uuid[]) AND is_deleted = false
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetWikisByIDs(ctx context.Context, dollar_1 []pgtype.UUID) ([]Wiki, error) {
|
func (q *Queries) GetWikisByIDs(ctx context.Context, dollar_1 []pgtype.UUID) ([]Wiki, error) {
|
||||||
@@ -215,7 +383,6 @@ func (q *Queries) GetWikisByIDs(ctx context.Context, dollar_1 []pgtype.UUID) ([]
|
|||||||
&i.ProjectID,
|
&i.ProjectID,
|
||||||
&i.Title,
|
&i.Title,
|
||||||
&i.Slug,
|
&i.Slug,
|
||||||
&i.Content,
|
|
||||||
&i.IsDeleted,
|
&i.IsDeleted,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
@@ -231,7 +398,7 @@ func (q *Queries) GetWikisByIDs(ctx context.Context, dollar_1 []pgtype.UUID) ([]
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getWikisByProjectId = `-- name: GetWikisByProjectId :many
|
const getWikisByProjectId = `-- name: GetWikisByProjectId :many
|
||||||
SELECT id, project_id, title, slug, content, is_deleted, created_at, updated_at
|
SELECT id, project_id, title, slug, is_deleted, created_at, updated_at
|
||||||
FROM wikis
|
FROM wikis
|
||||||
WHERE project_id = $1 AND is_deleted = false
|
WHERE project_id = $1 AND is_deleted = false
|
||||||
`
|
`
|
||||||
@@ -250,7 +417,6 @@ func (q *Queries) GetWikisByProjectId(ctx context.Context, projectID pgtype.UUID
|
|||||||
&i.ProjectID,
|
&i.ProjectID,
|
||||||
&i.Title,
|
&i.Title,
|
||||||
&i.Slug,
|
&i.Slug,
|
||||||
&i.Content,
|
|
||||||
&i.IsDeleted,
|
&i.IsDeleted,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
@@ -266,7 +432,7 @@ func (q *Queries) GetWikisByProjectId(ctx context.Context, projectID pgtype.UUID
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getWikisBySlugs = `-- name: GetWikisBySlugs :many
|
const getWikisBySlugs = `-- name: GetWikisBySlugs :many
|
||||||
SELECT id, project_id, title, slug, content, is_deleted, created_at, updated_at FROM wikis WHERE slug = ANY($1::text[]) AND is_deleted = false
|
SELECT id, project_id, title, slug, is_deleted, created_at, updated_at FROM wikis WHERE slug = ANY($1::text[]) AND is_deleted = false
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetWikisBySlugs(ctx context.Context, dollar_1 []string) ([]Wiki, error) {
|
func (q *Queries) GetWikisBySlugs(ctx context.Context, dollar_1 []string) ([]Wiki, error) {
|
||||||
@@ -283,7 +449,6 @@ func (q *Queries) GetWikisBySlugs(ctx context.Context, dollar_1 []string) ([]Wik
|
|||||||
&i.ProjectID,
|
&i.ProjectID,
|
||||||
&i.Title,
|
&i.Title,
|
||||||
&i.Slug,
|
&i.Slug,
|
||||||
&i.Content,
|
|
||||||
&i.IsDeleted,
|
&i.IsDeleted,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
@@ -299,7 +464,7 @@ func (q *Queries) GetWikisBySlugs(ctx context.Context, dollar_1 []string) ([]Wik
|
|||||||
}
|
}
|
||||||
|
|
||||||
const searchWikis = `-- name: SearchWikis :many
|
const searchWikis = `-- name: SearchWikis :many
|
||||||
SELECT w.id, w.project_id, w.title, w.slug, w.content, w.is_deleted, w.created_at, w.updated_at
|
SELECT w.id, w.project_id, w.title, w.slug, w.is_deleted, w.created_at, w.updated_at
|
||||||
FROM wikis w
|
FROM wikis w
|
||||||
WHERE w.is_deleted = false
|
WHERE w.is_deleted = false
|
||||||
AND ($1::uuid IS NULL OR w.project_id = $1::uuid)
|
AND ($1::uuid IS NULL OR w.project_id = $1::uuid)
|
||||||
@@ -347,7 +512,6 @@ func (q *Queries) SearchWikis(ctx context.Context, arg SearchWikisParams) ([]Wik
|
|||||||
&i.ProjectID,
|
&i.ProjectID,
|
||||||
&i.Title,
|
&i.Title,
|
||||||
&i.Slug,
|
&i.Slug,
|
||||||
&i.Content,
|
|
||||||
&i.IsDeleted,
|
&i.IsDeleted,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
@@ -367,16 +531,14 @@ UPDATE wikis
|
|||||||
SET
|
SET
|
||||||
title = COALESCE($1, title),
|
title = COALESCE($1, title),
|
||||||
slug = COALESCE($2, slug),
|
slug = COALESCE($2, slug),
|
||||||
content = COALESCE($3, content),
|
project_id = COALESCE($3, project_id)
|
||||||
project_id = COALESCE($4, project_id)
|
WHERE id = $4 AND is_deleted = false
|
||||||
WHERE id = $5 AND is_deleted = false
|
RETURNING id, project_id, title, slug, is_deleted, created_at, updated_at
|
||||||
RETURNING id, project_id, title, slug, content, is_deleted, created_at, updated_at
|
|
||||||
`
|
`
|
||||||
|
|
||||||
type UpdateWikiParams struct {
|
type UpdateWikiParams struct {
|
||||||
Title pgtype.Text `json:"title"`
|
Title pgtype.Text `json:"title"`
|
||||||
Slug pgtype.Text `json:"slug"`
|
Slug pgtype.Text `json:"slug"`
|
||||||
Content pgtype.Text `json:"content"`
|
|
||||||
ProjectID pgtype.UUID `json:"project_id"`
|
ProjectID pgtype.UUID `json:"project_id"`
|
||||||
ID pgtype.UUID `json:"id"`
|
ID pgtype.UUID `json:"id"`
|
||||||
}
|
}
|
||||||
@@ -385,7 +547,6 @@ func (q *Queries) UpdateWiki(ctx context.Context, arg UpdateWikiParams) (Wiki, e
|
|||||||
row := q.db.QueryRow(ctx, updateWiki,
|
row := q.db.QueryRow(ctx, updateWiki,
|
||||||
arg.Title,
|
arg.Title,
|
||||||
arg.Slug,
|
arg.Slug,
|
||||||
arg.Content,
|
|
||||||
arg.ProjectID,
|
arg.ProjectID,
|
||||||
arg.ID,
|
arg.ID,
|
||||||
)
|
)
|
||||||
@@ -395,7 +556,6 @@ func (q *Queries) UpdateWiki(ctx context.Context, arg UpdateWikiParams) (Wiki, e
|
|||||||
&i.ProjectID,
|
&i.ProjectID,
|
||||||
&i.Title,
|
&i.Title,
|
||||||
&i.Slug,
|
&i.Slug,
|
||||||
&i.Content,
|
|
||||||
&i.IsDeleted,
|
&i.IsDeleted,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
|||||||
@@ -5,11 +5,17 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type WikiContentSample struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
CreatedAt *time.Time `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
type WikiEntity struct {
|
type WikiEntity struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Slug string `json:"slug"`
|
Slug string `json:"slug"`
|
||||||
Content string `json:"content"`
|
ContentSample []WikiContentSample `json:"content_sample"`
|
||||||
ProjectID string `json:"project_id"`
|
ProjectID string `json:"project_id"`
|
||||||
IsDeleted bool `json:"is_deleted"`
|
IsDeleted bool `json:"is_deleted"`
|
||||||
CreatedAt *time.Time `json:"created_at"`
|
CreatedAt *time.Time `json:"created_at"`
|
||||||
@@ -20,11 +26,21 @@ func (w *WikiEntity) ToResponse() *response.WikiResponse {
|
|||||||
if w == nil {
|
if w == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var contentSample []response.WikiContentSample
|
||||||
|
for _, c := range w.ContentSample {
|
||||||
|
contentSample = append(contentSample, response.WikiContentSample{
|
||||||
|
ID: c.ID,
|
||||||
|
Title: c.Title,
|
||||||
|
CreatedAt: c.CreatedAt,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return &response.WikiResponse{
|
return &response.WikiResponse{
|
||||||
ID: w.ID,
|
ID: w.ID,
|
||||||
Title: w.Title,
|
Title: w.Title,
|
||||||
Slug: w.Slug,
|
Slug: w.Slug,
|
||||||
Content: w.Content,
|
ContentSample: contentSample,
|
||||||
ProjectID: w.ProjectID,
|
ProjectID: w.ProjectID,
|
||||||
IsDeleted: w.IsDeleted,
|
IsDeleted: w.IsDeleted,
|
||||||
CreatedAt: w.CreatedAt,
|
CreatedAt: w.CreatedAt,
|
||||||
@@ -45,3 +61,12 @@ func WikisEntityToResponse(ws []*WikiEntity) []*response.WikiResponse {
|
|||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WikiContentEntity struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
WikiID string `json:"wiki_id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
IsDeleted bool `json:"is_deleted"`
|
||||||
|
CreatedAt *time.Time `json:"created_at"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ type WikiRepository interface {
|
|||||||
BulkDeleteEntityWikisByWikiID(ctx context.Context, wikiID pgtype.UUID) error
|
BulkDeleteEntityWikisByWikiID(ctx context.Context, wikiID pgtype.UUID) error
|
||||||
DeleteEntityWiki(ctx context.Context, entityID pgtype.UUID, wikiID pgtype.UUID) error
|
DeleteEntityWiki(ctx context.Context, entityID pgtype.UUID, wikiID pgtype.UUID) error
|
||||||
DeleteEntityWikisByProjectID(ctx context.Context, projectID pgtype.UUID) error
|
DeleteEntityWikisByProjectID(ctx context.Context, projectID pgtype.UUID) error
|
||||||
|
CreateContent(ctx context.Context, params sqlc.CreateWikiContentParams) (*models.WikiContentEntity, error)
|
||||||
|
GetContentCountByWikiID(ctx context.Context, wikiID pgtype.UUID) (int64, error)
|
||||||
|
GetContentByID(ctx context.Context, id pgtype.UUID) (*models.WikiContentEntity, error)
|
||||||
|
GetContentByIDs(ctx context.Context, ids []string) ([]*models.WikiContentEntity, error)
|
||||||
WithTx(tx pgx.Tx) WikiRepository
|
WithTx(tx pgx.Tx) WikiRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +97,6 @@ func (r *wikiRepository) getByIDsWithFallback(ctx context.Context, ids []string)
|
|||||||
ID: convert.UUIDToString(row.ID),
|
ID: convert.UUIDToString(row.ID),
|
||||||
Title: convert.TextToString(row.Title),
|
Title: convert.TextToString(row.Title),
|
||||||
Slug: convert.TextToString(row.Slug),
|
Slug: convert.TextToString(row.Slug),
|
||||||
Content: convert.TextToString(row.Content),
|
|
||||||
IsDeleted: row.IsDeleted,
|
IsDeleted: row.IsDeleted,
|
||||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||||
@@ -101,6 +104,20 @@ func (r *wikiRepository) getByIDsWithFallback(ctx context.Context, ids []string)
|
|||||||
}
|
}
|
||||||
dbMap[item.ID] = &item
|
dbMap[item.ID] = &item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
samples, err := r.q.GetWikiContentByWikiIDs(ctx, missingPgIds)
|
||||||
|
if err == nil {
|
||||||
|
for _, sample := range samples {
|
||||||
|
wikiID := convert.UUIDToString(sample.WikiID)
|
||||||
|
if item, ok := dbMap[wikiID]; ok {
|
||||||
|
item.ContentSample = append(item.ContentSample, models.WikiContentSample{
|
||||||
|
ID: convert.UUIDToString(sample.ID),
|
||||||
|
Title: sample.Title,
|
||||||
|
CreatedAt: convert.TimeToPtr(sample.CreatedAt),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,12 +164,24 @@ func (r *wikiRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.W
|
|||||||
ID: convert.UUIDToString(row.ID),
|
ID: convert.UUIDToString(row.ID),
|
||||||
Title: convert.TextToString(row.Title),
|
Title: convert.TextToString(row.Title),
|
||||||
Slug: convert.TextToString(row.Slug),
|
Slug: convert.TextToString(row.Slug),
|
||||||
Content: convert.TextToString(row.Content),
|
|
||||||
IsDeleted: row.IsDeleted,
|
IsDeleted: row.IsDeleted,
|
||||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch content samples
|
||||||
|
samples, err := r.q.GetWikiContentByWikiID(ctx, row.ID)
|
||||||
|
if err == nil {
|
||||||
|
for _, sample := range samples {
|
||||||
|
wiki.ContentSample = append(wiki.ContentSample, models.WikiContentSample{
|
||||||
|
ID: convert.UUIDToString(sample.ID),
|
||||||
|
Title: sample.Title,
|
||||||
|
CreatedAt: convert.TimeToPtr(sample.CreatedAt),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_ = r.c.Set(ctx, cacheId, wiki, constants.NormalCacheDuration)
|
_ = r.c.Set(ctx, cacheId, wiki, constants.NormalCacheDuration)
|
||||||
|
|
||||||
return &wiki, nil
|
return &wiki, nil
|
||||||
@@ -178,7 +207,6 @@ func (r *wikiRepository) Search(ctx context.Context, params sqlc.SearchWikisPara
|
|||||||
ID: convert.UUIDToString(row.ID),
|
ID: convert.UUIDToString(row.ID),
|
||||||
Title: convert.TextToString(row.Title),
|
Title: convert.TextToString(row.Title),
|
||||||
Slug: convert.TextToString(row.Slug),
|
Slug: convert.TextToString(row.Slug),
|
||||||
Content: convert.TextToString(row.Content),
|
|
||||||
IsDeleted: row.IsDeleted,
|
IsDeleted: row.IsDeleted,
|
||||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||||
@@ -209,7 +237,6 @@ func (r *wikiRepository) Create(ctx context.Context, params sqlc.CreateWikiParam
|
|||||||
ID: convert.UUIDToString(row.ID),
|
ID: convert.UUIDToString(row.ID),
|
||||||
Title: convert.TextToString(row.Title),
|
Title: convert.TextToString(row.Title),
|
||||||
Slug: convert.TextToString(row.Slug),
|
Slug: convert.TextToString(row.Slug),
|
||||||
Content: convert.TextToString(row.Content),
|
|
||||||
IsDeleted: row.IsDeleted,
|
IsDeleted: row.IsDeleted,
|
||||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||||
@@ -228,7 +255,6 @@ func (r *wikiRepository) Update(ctx context.Context, params sqlc.UpdateWikiParam
|
|||||||
ID: convert.UUIDToString(row.ID),
|
ID: convert.UUIDToString(row.ID),
|
||||||
Title: convert.TextToString(row.Title),
|
Title: convert.TextToString(row.Title),
|
||||||
Slug: convert.TextToString(row.Slug),
|
Slug: convert.TextToString(row.Slug),
|
||||||
Content: convert.TextToString(row.Content),
|
|
||||||
IsDeleted: row.IsDeleted,
|
IsDeleted: row.IsDeleted,
|
||||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||||
@@ -285,7 +311,6 @@ func (r *wikiRepository) GetByProjectID(ctx context.Context, projectID pgtype.UU
|
|||||||
ID: convert.UUIDToString(row.ID),
|
ID: convert.UUIDToString(row.ID),
|
||||||
Title: convert.TextToString(row.Title),
|
Title: convert.TextToString(row.Title),
|
||||||
Slug: convert.TextToString(row.Slug),
|
Slug: convert.TextToString(row.Slug),
|
||||||
Content: convert.TextToString(row.Content),
|
|
||||||
IsDeleted: row.IsDeleted,
|
IsDeleted: row.IsDeleted,
|
||||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||||
@@ -350,7 +375,6 @@ func (r *wikiRepository) GetBySlug(ctx context.Context, slug string) (*models.Wi
|
|||||||
ID: convert.UUIDToString(row.ID),
|
ID: convert.UUIDToString(row.ID),
|
||||||
Title: convert.TextToString(row.Title),
|
Title: convert.TextToString(row.Title),
|
||||||
Slug: convert.TextToString(row.Slug),
|
Slug: convert.TextToString(row.Slug),
|
||||||
Content: convert.TextToString(row.Content),
|
|
||||||
IsDeleted: row.IsDeleted,
|
IsDeleted: row.IsDeleted,
|
||||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||||
@@ -390,7 +414,6 @@ func (r *wikiRepository) GetBySlugs(ctx context.Context, slugs []string) ([]*mod
|
|||||||
ID: convert.UUIDToString(row.ID),
|
ID: convert.UUIDToString(row.ID),
|
||||||
Title: convert.TextToString(row.Title),
|
Title: convert.TextToString(row.Title),
|
||||||
Slug: convert.TextToString(row.Slug),
|
Slug: convert.TextToString(row.Slug),
|
||||||
Content: convert.TextToString(row.Content),
|
|
||||||
IsDeleted: row.IsDeleted,
|
IsDeleted: row.IsDeleted,
|
||||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||||
@@ -421,3 +444,117 @@ func (r *wikiRepository) GetBySlugs(ctx context.Context, slugs []string) ([]*mod
|
|||||||
|
|
||||||
return wikis, nil
|
return wikis, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *wikiRepository) CreateContent(ctx context.Context, params sqlc.CreateWikiContentParams) (*models.WikiContentEntity, error) {
|
||||||
|
row, err := r.q.CreateWikiContent(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &models.WikiContentEntity{
|
||||||
|
ID: convert.UUIDToString(row.ID),
|
||||||
|
WikiID: convert.UUIDToString(row.WikiID),
|
||||||
|
Title: row.Title,
|
||||||
|
Content: convert.TextToString(row.Content),
|
||||||
|
IsDeleted: row.IsDeleted,
|
||||||
|
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *wikiRepository) GetContentCountByWikiID(ctx context.Context, wikiID pgtype.UUID) (int64, error) {
|
||||||
|
return r.q.GetWikiContentCount(ctx, wikiID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *wikiRepository) getContentByIDsWithFallback(ctx context.Context, ids []string) ([]*models.WikiContentEntity, error) {
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return []*models.WikiContentEntity{}, nil
|
||||||
|
}
|
||||||
|
keys := make([]string, len(ids))
|
||||||
|
for i, id := range ids {
|
||||||
|
keys[i] = fmt.Sprintf("wiki_content:id:%s", id)
|
||||||
|
}
|
||||||
|
raws := r.c.MGet(ctx, keys...)
|
||||||
|
|
||||||
|
var contents []*models.WikiContentEntity
|
||||||
|
missingToCache := make(map[string]any)
|
||||||
|
|
||||||
|
var missingPgIds []pgtype.UUID
|
||||||
|
for i, b := range raws {
|
||||||
|
if len(b) == 0 {
|
||||||
|
pgId := pgtype.UUID{}
|
||||||
|
err := pgId.Scan(ids[i])
|
||||||
|
if err == nil {
|
||||||
|
missingPgIds = append(missingPgIds, pgId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbMap := make(map[string]*models.WikiContentEntity)
|
||||||
|
if len(missingPgIds) > 0 {
|
||||||
|
dbRows, err := r.q.GetWikiContentByIDs(ctx, missingPgIds)
|
||||||
|
if err == nil {
|
||||||
|
for _, row := range dbRows {
|
||||||
|
item := models.WikiContentEntity{
|
||||||
|
ID: convert.UUIDToString(row.ID),
|
||||||
|
WikiID: convert.UUIDToString(row.WikiID),
|
||||||
|
Title: row.Title,
|
||||||
|
Content: convert.TextToString(row.Content),
|
||||||
|
IsDeleted: row.IsDeleted,
|
||||||
|
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||||
|
}
|
||||||
|
dbMap[item.ID] = &item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, b := range raws {
|
||||||
|
if len(b) > 0 {
|
||||||
|
var u models.WikiContentEntity
|
||||||
|
if err := json.Unmarshal(b, &u); err == nil {
|
||||||
|
contents = append(contents, &u)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if item, ok := dbMap[ids[i]]; ok {
|
||||||
|
contents = append(contents, item)
|
||||||
|
missingToCache[keys[i]] = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(missingToCache) > 0 {
|
||||||
|
_ = r.c.MSet(ctx, missingToCache, constants.NormalCacheDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
return contents, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *wikiRepository) GetContentByID(ctx context.Context, id pgtype.UUID) (*models.WikiContentEntity, error) {
|
||||||
|
cacheId := fmt.Sprintf("wiki_content:id:%s", convert.UUIDToString(id))
|
||||||
|
var content models.WikiContentEntity
|
||||||
|
err := r.c.Get(ctx, cacheId, &content)
|
||||||
|
if err == nil {
|
||||||
|
_ = r.c.Set(ctx, cacheId, content, constants.NormalCacheDuration)
|
||||||
|
return &content, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
row, err := r.q.GetWikiContentById(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
content = models.WikiContentEntity{
|
||||||
|
ID: convert.UUIDToString(row.ID),
|
||||||
|
WikiID: convert.UUIDToString(row.WikiID),
|
||||||
|
Title: row.Title,
|
||||||
|
Content: convert.TextToString(row.Content),
|
||||||
|
IsDeleted: row.IsDeleted,
|
||||||
|
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||||
|
}
|
||||||
|
_ = r.c.Set(ctx, cacheId, content, constants.NormalCacheDuration)
|
||||||
|
|
||||||
|
return &content, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *wikiRepository) GetContentByIDs(ctx context.Context, ids []string) ([]*models.WikiContentEntity, error) {
|
||||||
|
return r.getContentByIDsWithFallback(ctx, ids)
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ func WikiRoutes(router fiber.Router, wikiController *controllers.WikiController)
|
|||||||
wiki.Get("/", wikiController.SearchWikis)
|
wiki.Get("/", wikiController.SearchWikis)
|
||||||
wiki.Get("/slug/exists", wikiController.IsExistWikiSlug)
|
wiki.Get("/slug/exists", wikiController.IsExistWikiSlug)
|
||||||
wiki.Get("/slug/:slug", wikiController.GetWikiBySlug)
|
wiki.Get("/slug/:slug", wikiController.GetWikiBySlug)
|
||||||
|
wiki.Get("/content/:id", wikiController.GetWikiContentById)
|
||||||
wiki.Get("/:id", wikiController.GetWikiById)
|
wiki.Get("/:id", wikiController.GetWikiById)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -512,12 +512,29 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
|||||||
ID: wikiUUID,
|
ID: wikiUUID,
|
||||||
Title: convert.StringToText(wiki.Title),
|
Title: convert.StringToText(wiki.Title),
|
||||||
Slug: convert.PtrToText(wiki.Slug),
|
Slug: convert.PtrToText(wiki.Slug),
|
||||||
Content: convert.StringToText(wiki.Doc),
|
|
||||||
ProjectID: projectUUID,
|
ProjectID: projectUUID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update wiki: "+err.Error())
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update wiki: "+err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
count, err := s.wikiRepo.GetContentCountByWikiID(ctx, wikiUUID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to get wiki content count: "+err.Error())
|
||||||
|
}
|
||||||
|
versionTitle := fmt.Sprintf("Version %d", count+1)
|
||||||
|
|
||||||
|
_, err = wikiRepo.CreateContent(ctx, sqlc.CreateWikiContentParams{
|
||||||
|
WikiID: wikiUUID,
|
||||||
|
Title: versionTitle,
|
||||||
|
Content: convert.StringToText(wiki.Doc),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create wiki content: "+err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = s.c.Del(ctx, fmt.Sprintf("wiki:id:%s", wikiUUID.String()), fmt.Sprintf("wiki:slug:%s", *wiki.Slug))
|
||||||
|
|
||||||
newWikis = append(newWikis, snapshotData.Wikis[i])
|
newWikis = append(newWikis, snapshotData.Wikis[i])
|
||||||
|
|
||||||
} else if wiki.Source == "inline" {
|
} else if wiki.Source == "inline" {
|
||||||
@@ -525,12 +542,23 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
|||||||
ID: wikiUUID,
|
ID: wikiUUID,
|
||||||
Title: convert.StringToText(wiki.Title),
|
Title: convert.StringToText(wiki.Title),
|
||||||
Slug: convert.PtrToText(wiki.Slug),
|
Slug: convert.PtrToText(wiki.Slug),
|
||||||
Content: convert.StringToText(wiki.Doc),
|
|
||||||
ProjectID: projectUUID,
|
ProjectID: projectUUID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create wiki: "+err.Error())
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create wiki: "+err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, err = wikiRepo.CreateContent(ctx, sqlc.CreateWikiContentParams{
|
||||||
|
WikiID: wikiUUID,
|
||||||
|
Title: "Version 1",
|
||||||
|
Content: convert.StringToText(wiki.Doc),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create wiki content: "+err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = s.c.Del(ctx, fmt.Sprintf("wiki:id:%s", wikiUUID.String()), fmt.Sprintf("wiki:slug:%s", *wiki.Slug))
|
||||||
|
|
||||||
newWikis = append(newWikis, snapshotData.Wikis[i])
|
newWikis = append(newWikis, snapshotData.Wikis[i])
|
||||||
|
|
||||||
} else if wiki.Source == "ref" {
|
} else if wiki.Source == "ref" {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type WikiService interface {
|
|||||||
GetWikiBySlug(ctx context.Context, slug string) (*response.WikiResponse, *fiber.Error)
|
GetWikiBySlug(ctx context.Context, slug string) (*response.WikiResponse, *fiber.Error)
|
||||||
IsExistWikiSlug(ctx context.Context, slug string) (bool, *fiber.Error)
|
IsExistWikiSlug(ctx context.Context, slug string) (bool, *fiber.Error)
|
||||||
SearchWikis(ctx context.Context, req *request.SearchWikiDto) ([]*response.WikiResponse, *fiber.Error)
|
SearchWikis(ctx context.Context, req *request.SearchWikiDto) ([]*response.WikiResponse, *fiber.Error)
|
||||||
|
GetWikiContentByID(ctx context.Context, id string) (*response.WikiContentResponse, *fiber.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type wikiService struct {
|
type wikiService struct {
|
||||||
@@ -107,3 +108,22 @@ func (s *wikiService) SearchWikis(ctx context.Context, req *request.SearchWikiDt
|
|||||||
|
|
||||||
return models.WikisEntityToResponse(wikis), nil
|
return models.WikisEntityToResponse(wikis), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *wikiService) GetWikiContentByID(ctx context.Context, id string) (*response.WikiContentResponse, *fiber.Error) {
|
||||||
|
contentId, err := convert.StringToUUID(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid content ID format")
|
||||||
|
}
|
||||||
|
content, err := s.wikiRepo.GetContentByID(ctx, contentId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusNotFound, "Wiki content not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &response.WikiContentResponse{
|
||||||
|
ID: content.ID,
|
||||||
|
WikiID: content.WikiID,
|
||||||
|
Title: content.Title,
|
||||||
|
Content: content.Content,
|
||||||
|
CreatedAt: content.CreatedAt,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user