Files
Work/Backend/internal/bootstrap/project_queries.go
T

347 lines
9.8 KiB
Go

// 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.cbor'
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.node_kind = 'file'::posix_node_kind
AND (
(directories.logical_type = 'hierarchy_folder' AND node_meta.path = directories.path || '/folder.cbor')
OR (directories.logical_type = 'item' AND node_meta.path = directories.path || '/item.cbor')
)
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()
}