Skip to content

Commit

Permalink
Fixed DEVRhylme-Foundation#23: Improve Error Handling in API Response
Browse files Browse the repository at this point in the history
  • Loading branch information
ayanAhm4d committed Feb 1, 2025
1 parent 7e57b5d commit 6e5cfc9
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 56 deletions.
44 changes: 28 additions & 16 deletions cmd/expressify/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,49 @@ package main
import (
"fmt"
"os"
"github.com/charmbracelet/bubbletea"

tea "github.com/charmbracelet/bubbletea"
"github.com/codersgyan/expressify/internal/cli_model"
"github.com/codersgyan/expressify/internal/structure" // Ensure this package is imported for CopyDir
"github.com/codersgyan/expressify/internal/errors"
"github.com/codersgyan/expressify/internal/structure"
)

func main() {
handleCLI()
}

// Get current working directory
func handleCLI() {
cwd, err := os.Getwd()
if err != nil {
fmt.Printf("Unable to get current working directory: %v\n", err)
os.Exit(1)
handleError(errors.NewSystemError(
"Failed to get working directory",
fmt.Sprintf("Error details: %v", err),
))
return
}

// Define source and destination paths for the directory copy
srcPath := cwd + "/.templates/jsbase"
dstPath := cwd + "/.expressify/auth-service"

// Copy the directory
cpErr := structure.CopyDir(srcPath, dstPath)
if cpErr != nil {
fmt.Printf("Error copying directory: %s\n", cpErr)
os.Exit(1)
} else {
fmt.Println("Directory copied successfully.")
if err := structure.CopyDir(srcPath, dstPath); err != nil {
handleError(err)
return
}

// Run the CLI program using bubbletea
p := tea.NewProgram(cli_model.InitialModel())
if _, err := p.Run(); err != nil {
fmt.Printf("Alas, there's been an error: %v", err)
os.Exit(1)
handleError(errors.NewRuntimeError(
"CLI program error",
fmt.Sprintf("Error running CLI: %v", err),
))
return
}
}

func handleError(err *errors.AppError) {
fmt.Printf("\nError Type: %s\n", err.Type)
fmt.Printf("Message: %s\n", err.Message)
fmt.Printf("Details: %s\n", err.Detail)
fmt.Printf("Code: %d\n", err.Code)
os.Exit(1)
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ go 1.21.3
require (
github.com/charmbracelet/bubbles v0.17.1
github.com/charmbracelet/bubbletea v0.25.0
github.com/charmbracelet/lipgloss v0.9.1
)

require (
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/lipgloss v0.9.1 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
Expand Down
11 changes: 8 additions & 3 deletions internal/databases/databases.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ const (
MySQL Database = "MySQL"
)

// Implement the list.Item interface for Database type
func (d Database) FilterValue() string {
return string(d)
}

var databases = []list.Item{
list.Item(MongoDB),
list.Item(PostgreSQL),
list.Item(MySQL),
MongoDB,
PostgreSQL,
MySQL,
}

func NewDatabaseSelector() *selector.Selector {
Expand Down
48 changes: 48 additions & 0 deletions internal/errors/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package errors

import "fmt"

// ErrorType represents the type of error that occurred
type ErrorType string

const (
ValidationError ErrorType = "VALIDATION_ERROR"
RuntimeError ErrorType = "RUNTIME_ERROR"
SystemError ErrorType = "SYSTEM_ERROR"
)

// AppError represents a structured error response
type AppError struct {
Type ErrorType `json:"type"`
Message string `json:"message"`
Detail string `json:"detail"`
Code int `json:"code"`
}

// Error implements the error interface
func (e *AppError) Error() string {
return fmt.Sprintf("[%s] %s: %s", e.Type, e.Message, e.Detail)
}

// New creates a new AppError
func New(errorType ErrorType, message string, detail string, code int) *AppError {
return &AppError{
Type: errorType,
Message: message,
Detail: detail,
Code: code,
}
}

// Common error constructors
func NewValidationError(message string, detail string) *AppError {
return New(ValidationError, message, detail, 400)
}

func NewRuntimeError(message string, detail string) *AppError {
return New(RuntimeError, message, detail, 500)
}

func NewSystemError(message string, detail string) *AppError {
return New(SystemError, message, detail, 500)
}
118 changes: 82 additions & 36 deletions internal/structure/structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,99 +6,145 @@ import (
"os"
"path/filepath"

"github.com/codersgyan/expressify/internal/errors"
"github.com/codersgyan/expressify/internal/languages"
)

func CreateBaseFileStructure(projectName string, language string) error {
func CreateBaseFileStructure(projectName string, language string) *errors.AppError {
cwd, err := os.Getwd()
fmt.Println("Current Working Directory:", cwd)

if err != nil {
return fmt.Errorf("unable to get current working directory: %w", err)
return errors.NewSystemError(
"Failed to get working directory",
fmt.Sprintf("Error details: %v", err),
)
}

projectPath := filepath.Join(cwd, ".expressify", projectName)
if _, err := os.Stat(projectPath); !os.IsNotExist(err) {
return fmt.Errorf("folder \"%s\" already exists or cannot check existence", projectPath)
return errors.NewValidationError(
"Project already exists",
fmt.Sprintf("A project with name '%s' already exists at path: %s", projectName, projectPath),
)
}

if err := validateProjectName(projectName); err != nil {
return errors.NewValidationError(
"Invalid project name",
err.Error(),
)
}
mkdirProjectDirErr := os.Mkdir(projectPath, 0755) // 0755 is commonly used permissions

mkdirProjectDirErr := os.MkdirAll(projectPath, 0755)
if mkdirProjectDirErr != nil {
return fmt.Errorf("unable to create project folder: %w", err)
return errors.NewSystemError(
"Failed to create project directory",
fmt.Sprintf("Error creating directory at %s: %v", projectPath, mkdirProjectDirErr),
)
}

var languageWisePath string
if language == string(languages.JavaScript) {
languageWisePath = "jsbase"
} else {
} else if language == string(languages.TypeScript) {
languageWisePath = "tsbase"
} else {
return errors.NewValidationError(
"Invalid language selection",
fmt.Sprintf("Language '%s' is not supported. Use 'JavaScript' or 'TypeScript'", language),
)
}

srcPath := filepath.Join(cwd, ".templates", languageWisePath)
dstPath := filepath.Join(projectPath)

cpErr := CopyDir(srcPath, dstPath)
if cpErr != nil {
fmt.Printf("Error copying directory: %s\n", cpErr)
} else {
fmt.Println("Directory copied successfully.")
if err := CopyDir(srcPath, dstPath); err != nil {
return errors.NewSystemError(
"Failed to copy template files",
fmt.Sprintf("Error copying from %s to %s: %v", srcPath, dstPath, err),
)
}

return nil
}

func validateProjectName(name string) error {
if name == "" {
return fmt.Errorf("project name cannot be empty")
}
if len(name) > 214 {
return fmt.Errorf("project name too long (max 214 characters)")
}
// Add more validation rules as needed
return nil
}

func CopyFile(src, dst string) error {
func CopyFile(src, dst string) *errors.AppError {
sourceFile, err := os.Open(src)
if err != nil {
return err
return errors.NewSystemError(
"Failed to open source file",
fmt.Sprintf("Error opening %s: %v", src, err),
)
}
defer sourceFile.Close()

destFile, err := os.Create(dst)
if err != nil {
return err
return errors.NewSystemError(
"Failed to create destination file",
fmt.Sprintf("Error creating %s: %v", dst, err),
)
}
defer destFile.Close()

_, err = io.Copy(destFile, sourceFile)
return err
if _, err := io.Copy(destFile, sourceFile); err != nil {
return errors.NewSystemError(
"Failed to copy file contents",
fmt.Sprintf("Error copying from %s to %s: %v", src, dst, err),
)
}

return nil
}

// copyDir recursively copies a directory from src to dst.
func CopyDir(src, dst string) error {
// Get properties of source dir
func CopyDir(src, dst string) *errors.AppError {
info, err := os.Stat(src)
if err != nil {
return err
return errors.NewSystemError(
"Failed to get source directory info",
fmt.Sprintf("Error getting info for %s: %v", src, err),
)
}

// Create the destination directory
err = os.MkdirAll(dst, info.Mode())
if err != nil {
return err
if err := os.MkdirAll(dst, info.Mode()); err != nil {
return errors.NewSystemError(
"Failed to create destination directory",
fmt.Sprintf("Error creating directory at %s: %v", dst, err),
)
}

// Read directory contents
entries, err := os.ReadDir(src)
if err != nil {
return err
return errors.NewSystemError(
"Failed to read source directory",
fmt.Sprintf("Error reading directory %s: %v", src, err),
)
}

for _, entry := range entries {
srcPath := filepath.Join(src, entry.Name())
dstPath := filepath.Join(dst, entry.Name())

if entry.IsDir() {
// Recursive call for directories
err = CopyDir(srcPath, dstPath)
if err != nil {
return err
if err := CopyDir(srcPath, dstPath); err != nil {
return err // Already wrapped in AppError
}
} else {
// Copy files
err = CopyFile(srcPath, dstPath)
if err != nil {
return err
if err := CopyFile(srcPath, dstPath); err != nil {
return err // Already wrapped in AppError
}
}
}

return nil
}

0 comments on commit 6e5cfc9

Please sign in to comment.