Refactor: improve backend modularity
This commit is contained in:
@@ -0,0 +1,347 @@
|
||||
// Path: Backend/internal/bootstrap/project_queries.go
|
||||
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
func (service *Service) loadPrimaryOrganization(ctx context.Context) (*OrganizationRecord, error) {
|
||||
var record OrganizationRecord
|
||||
err := service.db.Pool.QueryRow(ctx, `
|
||||
SELECT id::text, name, slug
|
||||
FROM organizations
|
||||
ORDER BY CASE WHEN slug = $1 THEN 0 ELSE 1 END, created_at ASC
|
||||
LIMIT 1;
|
||||
`, primaryOrganizationSlug).Scan(&record.ID, &record.Name, &record.Slug)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
func (service *Service) loadPrimaryDepartment(ctx context.Context) (*DepartmentRecord, error) {
|
||||
var record DepartmentRecord
|
||||
err := service.db.Pool.QueryRow(ctx, `
|
||||
SELECT id::text, organization_id::text, name, slug
|
||||
FROM departments
|
||||
ORDER BY CASE WHEN slug = $1 THEN 0 ELSE 1 END, created_at ASC
|
||||
LIMIT 1;
|
||||
`, primaryDepartmentSlug).Scan(&record.ID, &record.OrganizationID, &record.Name, &record.Slug)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
func (service *Service) loadPrimaryTeam(ctx context.Context) (*TeamRecord, error) {
|
||||
var record TeamRecord
|
||||
err := service.db.Pool.QueryRow(ctx, `
|
||||
SELECT id::text, organization_id::text, department_id::text, name, slug
|
||||
FROM teams
|
||||
ORDER BY CASE WHEN slug = $1 THEN 0 ELSE 1 END, created_at ASC
|
||||
LIMIT 1;
|
||||
`, primaryTeamSlug).Scan(&record.ID, &record.OrganizationID, &record.DepartmentID, &record.Name, &record.Slug)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
func (service *Service) loadPrimaryProject(ctx context.Context) (*ProjectRecord, error) {
|
||||
var record ProjectRecord
|
||||
err := service.db.Pool.QueryRow(ctx, `
|
||||
SELECT id::text, organization_id::text, department_id::text, team_id::text, name, slug
|
||||
FROM projects
|
||||
ORDER BY CASE WHEN slug = $1 THEN 0 ELSE 1 END, created_at ASC
|
||||
LIMIT 1;
|
||||
`, primaryProjectSlug).Scan(&record.ID, &record.OrganizationID, &record.DepartmentID, &record.TeamID, &record.Name, &record.Slug)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
func (service *Service) listOrganizations(ctx context.Context) ([]OrganizationRecord, error) {
|
||||
rows, err := service.db.Pool.Query(ctx, `
|
||||
SELECT id::text, name, slug
|
||||
FROM organizations
|
||||
ORDER BY created_at ASC;
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var records []OrganizationRecord
|
||||
for rows.Next() {
|
||||
var record OrganizationRecord
|
||||
if err := rows.Scan(&record.ID, &record.Name, &record.Slug); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
records = append(records, record)
|
||||
}
|
||||
|
||||
return records, rows.Err()
|
||||
}
|
||||
|
||||
func (service *Service) listDepartments(ctx context.Context) ([]DepartmentRecord, error) {
|
||||
rows, err := service.db.Pool.Query(ctx, `
|
||||
SELECT id::text, organization_id::text, name, slug
|
||||
FROM departments
|
||||
ORDER BY created_at ASC;
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var records []DepartmentRecord
|
||||
for rows.Next() {
|
||||
var record DepartmentRecord
|
||||
if err := rows.Scan(&record.ID, &record.OrganizationID, &record.Name, &record.Slug); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
records = append(records, record)
|
||||
}
|
||||
|
||||
return records, rows.Err()
|
||||
}
|
||||
|
||||
func (service *Service) listTeams(ctx context.Context) ([]TeamRecord, error) {
|
||||
rows, err := service.db.Pool.Query(ctx, `
|
||||
SELECT id::text, organization_id::text, department_id::text, name, slug
|
||||
FROM teams
|
||||
ORDER BY created_at ASC;
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var records []TeamRecord
|
||||
for rows.Next() {
|
||||
var record TeamRecord
|
||||
if err := rows.Scan(&record.ID, &record.OrganizationID, &record.DepartmentID, &record.Name, &record.Slug); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
records = append(records, record)
|
||||
}
|
||||
|
||||
return records, rows.Err()
|
||||
}
|
||||
|
||||
func (service *Service) listProjects(ctx context.Context) ([]ProjectRecord, error) {
|
||||
rows, err := service.db.Pool.Query(ctx, `
|
||||
SELECT id::text, organization_id::text, department_id::text, team_id::text, name, slug
|
||||
FROM projects
|
||||
ORDER BY created_at ASC;
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var records []ProjectRecord
|
||||
for rows.Next() {
|
||||
var record ProjectRecord
|
||||
if err := rows.Scan(&record.ID, &record.OrganizationID, &record.DepartmentID, &record.TeamID, &record.Name, &record.Slug); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
records = append(records, record)
|
||||
}
|
||||
|
||||
return records, rows.Err()
|
||||
}
|
||||
|
||||
func (service *Service) loadProjectByID(ctx context.Context, projectID string) (*ProjectRecord, error) {
|
||||
var record ProjectRecord
|
||||
err := service.db.Pool.QueryRow(ctx, `
|
||||
SELECT id::text, organization_id::text, department_id::text, team_id::text, name, slug
|
||||
FROM projects
|
||||
WHERE id = $1::uuid
|
||||
LIMIT 1;
|
||||
`, projectID).Scan(&record.ID, &record.OrganizationID, &record.DepartmentID, &record.TeamID, &record.Name, &record.Slug)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrProjectNotFound
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
func (service *Service) GetProjectHierarchyFolders(ctx context.Context, projectID string) ([]ProjectHierarchyFolderRecord, error) {
|
||||
return service.getProjectHierarchyFoldersByRootPath(ctx, projectID, projectHierarchyRootPath)
|
||||
}
|
||||
|
||||
func (service *Service) GetProjectTreeFolders(ctx context.Context, projectID string) ([]ProjectHierarchyFolderRecord, error) {
|
||||
return service.getProjectHierarchyFoldersByRootPath(ctx, projectID, projectTreeRootPath)
|
||||
}
|
||||
|
||||
func (service *Service) GetProjectTreeNodes(ctx context.Context, projectID string) ([]ProjectTreeNodeRecord, error) {
|
||||
return service.getProjectTreeNodesByRootPath(ctx, projectID, projectTreeRootPath)
|
||||
}
|
||||
|
||||
func (service *Service) getProjectHierarchyFoldersByRootPath(
|
||||
ctx context.Context,
|
||||
projectID string,
|
||||
rootPath func(projectSlug string) string,
|
||||
) ([]ProjectHierarchyFolderRecord, error) {
|
||||
project, err := service.loadProjectByID(ctx, projectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rootParentPath := rootPath(project.Slug)
|
||||
|
||||
rows, err := service.db.Pool.Query(ctx, `
|
||||
SELECT
|
||||
COALESCE(folder_meta.resource_id, ''),
|
||||
directories.path,
|
||||
COALESCE(directories.parent_path, ''),
|
||||
COALESCE(folder_meta.resource_name, directories.resource_name, '')
|
||||
FROM posix_nodes AS directories
|
||||
LEFT JOIN posix_nodes AS folder_meta
|
||||
ON folder_meta.path = directories.path || '/folder.json'
|
||||
AND folder_meta.node_kind = 'file'::posix_node_kind
|
||||
WHERE directories.node_kind = 'directory'::posix_node_kind
|
||||
AND directories.logical_type = 'hierarchy_folder'
|
||||
AND directories.project_slug = $1
|
||||
AND directories.path LIKE $2
|
||||
ORDER BY directories.depth ASC, directories.path ASC;
|
||||
`, project.Slug, rootParentPath+"/%")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var folderRows []projectHierarchyFolderRow
|
||||
for rows.Next() {
|
||||
var row projectHierarchyFolderRow
|
||||
if err := rows.Scan(&row.ID, &row.Path, &row.ParentPath, &row.Label); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
folderRows = append(folderRows, row)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
folders := buildProjectHierarchyFolderTree(folderRows, rootParentPath)
|
||||
folderOrder := service.readProjectFolderOrder(project.Slug, rootParentPath)
|
||||
|
||||
return applyProjectHierarchyFolderOrdering(folders, folderOrder), nil
|
||||
}
|
||||
|
||||
func (service *Service) getProjectTreeNodesByRootPath(
|
||||
ctx context.Context,
|
||||
projectID string,
|
||||
rootPath func(projectSlug string) string,
|
||||
) ([]ProjectTreeNodeRecord, error) {
|
||||
project, err := service.loadProjectByID(ctx, projectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rootParentPath := rootPath(project.Slug)
|
||||
|
||||
rows, err := service.db.Pool.Query(ctx, `
|
||||
SELECT
|
||||
COALESCE(node_meta.resource_id, ''),
|
||||
directories.path,
|
||||
COALESCE(directories.parent_path, ''),
|
||||
COALESCE(node_meta.resource_name, directories.resource_name, ''),
|
||||
directories.logical_type,
|
||||
COALESCE(node_meta.content_json->>'type', directories.content_json->>'type', '')
|
||||
FROM posix_nodes AS directories
|
||||
LEFT JOIN posix_nodes AS node_meta
|
||||
ON node_meta.path = directories.path || CASE
|
||||
WHEN directories.logical_type = 'hierarchy_folder' THEN '/folder.json'
|
||||
WHEN directories.logical_type = 'item' THEN '/item.json'
|
||||
ELSE ''
|
||||
END
|
||||
AND node_meta.node_kind = 'file'::posix_node_kind
|
||||
WHERE directories.node_kind = 'directory'::posix_node_kind
|
||||
AND directories.logical_type IN ('hierarchy_folder', 'item')
|
||||
AND directories.project_slug = $1
|
||||
AND directories.path LIKE $2
|
||||
ORDER BY directories.depth ASC, directories.path ASC;
|
||||
`, project.Slug, rootParentPath+"/%")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var nodeRows []projectTreeNodeRow
|
||||
for rows.Next() {
|
||||
var row projectTreeNodeRow
|
||||
if err := rows.Scan(&row.ID, &row.Path, &row.ParentPath, &row.Label, &row.Kind, &row.ItemType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodeRows = append(nodeRows, row)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodes := buildProjectTreeNodeTree(nodeRows, rootParentPath)
|
||||
folderOrder := service.readProjectFolderOrder(project.Slug, rootParentPath)
|
||||
|
||||
return applyProjectTreeNodeOrdering(nodes, folderOrder), nil
|
||||
}
|
||||
|
||||
func (service *Service) listWorkspaces(ctx context.Context) ([]WorkspaceRecord, error) {
|
||||
rows, err := service.db.Pool.Query(ctx, `
|
||||
SELECT id::text, organization_id::text, name, slug, kind::text, department_id::text, team_id::text, project_id::text
|
||||
FROM workspaces
|
||||
ORDER BY created_at ASC;
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var records []WorkspaceRecord
|
||||
for rows.Next() {
|
||||
var record WorkspaceRecord
|
||||
if err := rows.Scan(&record.ID, &record.OrganizationID, &record.Name, &record.Slug, &record.Kind, &record.DepartmentID, &record.TeamID, &record.ProjectID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
records = append(records, record)
|
||||
}
|
||||
|
||||
return records, rows.Err()
|
||||
}
|
||||
Reference in New Issue
Block a user