Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 109 additions & 9 deletions internal/models/application.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,116 @@
package models

// ApplicationSettings represents the settings for a Coolify application
type ApplicationSettings struct {
ID int `json:"-" table:"-"`
ApplicationID int `json:"-" table:"-"`
IsStatic *bool `json:"is_static,omitempty"`
IsBuildServerEnabled *bool `json:"is_build_server_enabled,omitempty"`
IsPreserveRepositoryEnabled *bool `json:"is_preserve_repository_enabled,omitempty"`
IsAutoDeployEnabled *bool `json:"is_auto_deploy_enabled,omitempty"`
IsForceHTTPSEnabled *bool `json:"is_force_https_enabled,omitempty"`
IsDebugEnabled *bool `json:"is_debug_enabled,omitempty"`
IsPreviewDeploymentsEnabled *bool `json:"is_preview_deployments_enabled,omitempty"`
IsGitSubmodulesEnabled *bool `json:"is_git_submodules_enabled,omitempty"`
IsGitLFSEnabled *bool `json:"is_git_lfs_enabled,omitempty"`
CreatedAt string `json:"-" table:"-"`
UpdatedAt string `json:"-" table:"-"`
}

// Application represents a Coolify application
type Application struct {
ID int `json:"-" table:"-"`
UUID string `json:"uuid"`
Name string `json:"name"`
Description *string `json:"description,omitempty"`
Status string `json:"status"`
GitBranch *string `json:"git_branch,omitempty"`
FQDN *string `json:"fqdn,omitempty"`
CreatedAt string `json:"-" table:"-"`
UpdatedAt string `json:"-" table:"-"`
// Table-visible fields
UUID string `json:"uuid"`
Name string `json:"name"`
Description *string `json:"description,omitempty"`
Status string `json:"status"`
FQDN *string `json:"fqdn,omitempty"`
GitRepository *string `json:"git_repository,omitempty"`
GitBranch *string `json:"git_branch,omitempty"`
BuildPack *string `json:"build_pack,omitempty"`
PortsExposes *string `json:"ports_exposes,omitempty"`

// Git extended (JSON-only)
GitCommitSHA *string `json:"git_commit_sha,omitempty" table:"-"`
GitFullURL *string `json:"git_full_url,omitempty" table:"-"`

// Build configuration (JSON-only)
InstallCommand *string `json:"install_command,omitempty" table:"-"`
BuildCommand *string `json:"build_command,omitempty" table:"-"`
StartCommand *string `json:"start_command,omitempty" table:"-"`
BaseDirectory *string `json:"base_directory,omitempty" table:"-"`
PublishDirectory *string `json:"publish_directory,omitempty" table:"-"`
StaticImage *string `json:"static_image,omitempty" table:"-"`

// Docker configuration (JSON-only)
Dockerfile *string `json:"dockerfile,omitempty" table:"-"`
DockerfileLocation *string `json:"dockerfile_location,omitempty" table:"-"`
DockerRegistryImageName *string `json:"docker_registry_image_name,omitempty" table:"-"`
DockerRegistryImageTag *string `json:"docker_registry_image_tag,omitempty" table:"-"`
DockerCompose *string `json:"docker_compose,omitempty" table:"-"`
DockerComposeRaw *string `json:"docker_compose_raw,omitempty" table:"-"`
DockerComposeLocation *string `json:"docker_compose_location,omitempty" table:"-"`
CustomDockerRunOptions *string `json:"custom_docker_run_options,omitempty" table:"-"`
CustomLabels *string `json:"custom_labels,omitempty" table:"-"`
CustomNginxConfiguration *string `json:"custom_nginx_configuration,omitempty" table:"-"`

// Networking (JSON-only)
PortsMappings *string `json:"ports_mappings,omitempty" table:"-"`
Domains *string `json:"domains,omitempty" table:"-"`
Redirect *string `json:"redirect,omitempty" table:"-"`
PreviewURLTemplate *string `json:"preview_url_template,omitempty" table:"-"`

// Health checks (JSON-only)
HealthCheckEnabled *bool `json:"health_check_enabled,omitempty" table:"-"`
HealthCheckPath *string `json:"health_check_path,omitempty" table:"-"`
HealthCheckPort *string `json:"health_check_port,omitempty" table:"-"`
HealthCheckHost *string `json:"health_check_host,omitempty" table:"-"`
HealthCheckMethod *string `json:"health_check_method,omitempty" table:"-"`
HealthCheckScheme *string `json:"health_check_scheme,omitempty" table:"-"`
HealthCheckReturnCode *int `json:"health_check_return_code,omitempty" table:"-"`
HealthCheckResponseText *string `json:"health_check_response_text,omitempty" table:"-"`
HealthCheckInterval *int `json:"health_check_interval,omitempty" table:"-"`
HealthCheckTimeout *int `json:"health_check_timeout,omitempty" table:"-"`
HealthCheckRetries *int `json:"health_check_retries,omitempty" table:"-"`
HealthCheckStartPeriod *int `json:"health_check_start_period,omitempty" table:"-"`

// Resource limits (JSON-only)
LimitsCPUs *string `json:"limits_cpus,omitempty" table:"-"`
LimitsCPUShares *int `json:"limits_cpu_shares,omitempty" table:"-"`
LimitsCPUSet *string `json:"limits_cpuset,omitempty" table:"-"`
LimitsMemory *string `json:"limits_memory,omitempty" table:"-"`
LimitsMemoryReservation *string `json:"limits_memory_reservation,omitempty" table:"-"`
LimitsMemorySwap *string `json:"limits_memory_swap,omitempty" table:"-"`
LimitsMemorySwappiness *int `json:"limits_memory_swappiness,omitempty" table:"-"`

// Deployment hooks (JSON-only)
PreDeploymentCommand *string `json:"pre_deployment_command,omitempty" table:"-"`
PreDeploymentCommandContainer *string `json:"pre_deployment_command_container,omitempty" table:"-"`
PostDeploymentCommand *string `json:"post_deployment_command,omitempty" table:"-"`
PostDeploymentCommandContainer *string `json:"post_deployment_command_container,omitempty" table:"-"`

// Webhook secrets (JSON-only)
ManualWebhookSecretGitHub *string `json:"manual_webhook_secret_github,omitempty" table:"-" sensitive:"true"`
ManualWebhookSecretGitLab *string `json:"manual_webhook_secret_gitlab,omitempty" table:"-" sensitive:"true"`
ManualWebhookSecretBitbucket *string `json:"manual_webhook_secret_bitbucket,omitempty" table:"-" sensitive:"true"`
ManualWebhookSecretGitea *string `json:"manual_webhook_secret_gitea,omitempty" table:"-" sensitive:"true"`

// Misc (JSON-only)
WatchPaths *string `json:"watch_paths,omitempty" table:"-"`
SwarmReplicas *int `json:"swarm_replicas,omitempty" table:"-"`
ConfigHash *string `json:"config_hash,omitempty" table:"-"`

// Nested settings (JSON-only)
Settings *ApplicationSettings `json:"settings,omitempty" table:"-"`

// Hidden fields (not in JSON or table output)
ID int `json:"-" table:"-"`
EnvironmentID *int `json:"-" table:"-"`
DestinationID *int `json:"-" table:"-"`
SourceID *int `json:"-" table:"-"`
PrivateKeyID *int `json:"-" table:"-"`
CreatedAt string `json:"-" table:"-"`
UpdatedAt string `json:"-" table:"-"`
}

// ApplicationListItem represents a simplified application for list view
Expand Down
120 changes: 120 additions & 0 deletions internal/models/models_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,126 @@ func TestProject_UnmarshalFromFixture(t *testing.T) {
assert.Equal(t, "running", project.Environments[0].Applications[0].Status)
}

func TestApplication_MarshalUnmarshal(t *testing.T) {
desc := "Test application"
repo := "https://github.com/example/app"
branch := "main"
fqdn := "https://app.example.com"
buildPack := "nixpacks"
ports := "3000"
installCmd := "npm install"
buildCmd := "npm run build"
startCmd := "npm start"
limitsCPUs := "2"
limitsMemory := "512M"
healthPath := "/health"
isAutoDeployEnabled := true

app := Application{
ID: 1,
UUID: "app-uuid",
Name: "My App",
Description: &desc,
Status: "running",
FQDN: &fqdn,
GitRepository: &repo,
GitBranch: &branch,
BuildPack: &buildPack,
PortsExposes: &ports,
InstallCommand: &installCmd,
BuildCommand: &buildCmd,
StartCommand: &startCmd,
LimitsCPUs: &limitsCPUs,
LimitsMemory: &limitsMemory,
HealthCheckPath: &healthPath,
Settings: &ApplicationSettings{
IsAutoDeployEnabled: &isAutoDeployEnabled,
},
CreatedAt: "2024-01-01T00:00:00Z",
UpdatedAt: "2024-01-02T00:00:00Z",
}

// Marshal
data, err := json.Marshal(app)
require.NoError(t, err)

// Unmarshal
var unmarshaled Application
err = json.Unmarshal(data, &unmarshaled)
require.NoError(t, err)

assert.Equal(t, app.UUID, unmarshaled.UUID)
assert.Equal(t, app.Name, unmarshaled.Name)
assert.Equal(t, *app.GitRepository, *unmarshaled.GitRepository)
assert.Equal(t, *app.BuildPack, *unmarshaled.BuildPack)
assert.Equal(t, *app.InstallCommand, *unmarshaled.InstallCommand)
assert.Equal(t, *app.LimitsCPUs, *unmarshaled.LimitsCPUs)
assert.NotNil(t, unmarshaled.Settings)
assert.True(t, *unmarshaled.Settings.IsAutoDeployEnabled)
}

func TestApplication_UnmarshalFromFixture(t *testing.T) {
fixtureData, err := os.ReadFile(filepath.Join("..", "..", "test", "fixtures", "application.json"))
require.NoError(t, err)

var app Application
err = json.Unmarshal(fixtureData, &app)
require.NoError(t, err)

assert.Equal(t, "app-fixture-uuid-123", app.UUID)
assert.Equal(t, "My Web Application", app.Name)
assert.Equal(t, "running", app.Status)
assert.Equal(t, "https://app.example.com", *app.FQDN)
assert.Equal(t, "https://github.com/example/app", *app.GitRepository)
assert.Equal(t, "main", *app.GitBranch)
assert.Equal(t, "nixpacks", *app.BuildPack)
assert.Equal(t, "3000", *app.PortsExposes)
assert.Equal(t, "npm install", *app.InstallCommand)
assert.Equal(t, "npm run build", *app.BuildCommand)
assert.Equal(t, "npm start", *app.StartCommand)
assert.Equal(t, "/health", *app.HealthCheckPath)
assert.Equal(t, 200, *app.HealthCheckReturnCode)
assert.Equal(t, "2", *app.LimitsCPUs)
assert.Equal(t, "512M", *app.LimitsMemory)
assert.Equal(t, "npm run migrate", *app.PreDeploymentCommand)
assert.Equal(t, 1, *app.SwarmReplicas)
assert.Equal(t, "abc123", *app.ConfigHash)

// Nested settings
require.NotNil(t, app.Settings)
assert.NotNil(t, app.Settings.IsAutoDeployEnabled)
assert.True(t, *app.Settings.IsAutoDeployEnabled)
assert.True(t, *app.Settings.IsForceHTTPSEnabled)
assert.False(t, *app.Settings.IsStatic)
}

func TestApplication_JSONExcludesHiddenFields(t *testing.T) {
app := Application{
ID: 42,
UUID: "app-uuid",
Name: "Test App",
Status: "running",
EnvironmentID: intPtr(1),
DestinationID: intPtr(2),
CreatedAt: "2024-01-01T00:00:00Z",
UpdatedAt: "2024-01-02T00:00:00Z",
}

data, err := json.Marshal(app)
require.NoError(t, err)

jsonStr := string(data)
assert.NotContains(t, jsonStr, `"id"`)
assert.NotContains(t, jsonStr, `"environment_id"`)
assert.NotContains(t, jsonStr, `"destination_id"`)
assert.NotContains(t, jsonStr, `"created_at"`)
assert.NotContains(t, jsonStr, `"updated_at"`)
assert.Contains(t, jsonStr, `"uuid"`)
assert.Contains(t, jsonStr, `"name"`)
}

func intPtr(v int) *int { return &v }

func TestResource_MarshalUnmarshal(t *testing.T) {
resource := Resource{
ID: 1,
Expand Down
54 changes: 45 additions & 9 deletions internal/service/application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,17 +110,41 @@ func TestApplicationService_Get(t *testing.T) {
desc := "Test Application"
branch := "main"
fqdn := "test.example.com"
repo := "https://github.com/example/app"
buildPack := "nixpacks"
ports := "3000"
installCmd := "npm install"
buildCmd := "npm run build"
startCmd := "npm start"
healthPath := "/health"
limitsCPUs := "2"
limitsMemory := "512M"
preDeployCmd := "npm run migrate"
isAutoDeployEnabled := true

application := models.Application{
ID: 1,
UUID: "app-uuid-123",
Name: "Test App",
Description: &desc,
Status: "running",
GitBranch: &branch,
FQDN: &fqdn,
CreatedAt: "2024-01-01T00:00:00Z",
UpdatedAt: "2024-01-02T00:00:00Z",
ID: 1,
UUID: "app-uuid-123",
Name: "Test App",
Description: &desc,
Status: "running",
GitBranch: &branch,
FQDN: &fqdn,
GitRepository: &repo,
BuildPack: &buildPack,
PortsExposes: &ports,
InstallCommand: &installCmd,
BuildCommand: &buildCmd,
StartCommand: &startCmd,
HealthCheckPath: &healthPath,
LimitsCPUs: &limitsCPUs,
LimitsMemory: &limitsMemory,
PreDeploymentCommand: &preDeployCmd,
Settings: &models.ApplicationSettings{
IsAutoDeployEnabled: &isAutoDeployEnabled,
},
CreatedAt: "2024-01-01T00:00:00Z",
UpdatedAt: "2024-01-02T00:00:00Z",
}

server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Expand All @@ -144,6 +168,18 @@ func TestApplicationService_Get(t *testing.T) {
assert.Equal(t, "running", result.Status)
assert.Equal(t, "main", *result.GitBranch)
assert.Equal(t, "test.example.com", *result.FQDN)
assert.Equal(t, "https://github.com/example/app", *result.GitRepository)
assert.Equal(t, "nixpacks", *result.BuildPack)
assert.Equal(t, "3000", *result.PortsExposes)
assert.Equal(t, "npm install", *result.InstallCommand)
assert.Equal(t, "npm run build", *result.BuildCommand)
assert.Equal(t, "npm start", *result.StartCommand)
assert.Equal(t, "/health", *result.HealthCheckPath)
assert.Equal(t, "2", *result.LimitsCPUs)
assert.Equal(t, "512M", *result.LimitsMemory)
assert.Equal(t, "npm run migrate", *result.PreDeploymentCommand)
require.NotNil(t, result.Settings)
assert.True(t, *result.Settings.IsAutoDeployEnabled)
}

func TestApplicationService_Get_NotFound(t *testing.T) {
Expand Down
76 changes: 76 additions & 0 deletions test/fixtures/application.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{
"id": 1,
"uuid": "app-fixture-uuid-123",
"name": "My Web Application",
"description": "A comprehensive test fixture",
"status": "running",
"fqdn": "https://app.example.com",
"git_repository": "https://github.com/example/app",
"git_branch": "main",
"git_commit_sha": "abc123def456",
"git_full_url": "https://github.com/example/app.git",
"build_pack": "nixpacks",
"ports_exposes": "3000",
"ports_mappings": "3000:3000",
"domains": "app.example.com",
"redirect": "www",
"install_command": "npm install",
"build_command": "npm run build",
"start_command": "npm start",
"base_directory": "/",
"publish_directory": "/dist",
"dockerfile": null,
"dockerfile_location": "/Dockerfile",
"docker_registry_image_name": null,
"docker_registry_image_tag": null,
"custom_docker_run_options": null,
"custom_labels": null,
"custom_nginx_configuration": null,
"health_check_enabled": true,
"health_check_path": "/health",
"health_check_port": "3000",
"health_check_host": null,
"health_check_method": "GET",
"health_check_scheme": "http",
"health_check_return_code": 200,
"health_check_response_text": null,
"health_check_interval": 30,
"health_check_timeout": 5,
"health_check_retries": 3,
"health_check_start_period": 10,
"limits_cpus": "2",
"limits_memory": "512M",
"limits_memory_swap": "1G",
"limits_memory_swappiness": 60,
"limits_memory_reservation": "256M",
"limits_cpuset": null,
"limits_cpu_shares": 1024,
"pre_deployment_command": "npm run migrate",
"pre_deployment_command_container": null,
"post_deployment_command": null,
"post_deployment_command_container": null,
"watch_paths": null,
"swarm_replicas": 1,
"config_hash": "abc123",
"environment_id": 1,
"destination_id": 2,
"source_id": null,
"private_key_id": null,
"settings": {
"id": 1,
"application_id": 1,
"is_static": false,
"is_build_server_enabled": false,
"is_preserve_repository_enabled": false,
"is_auto_deploy_enabled": true,
"is_force_https_enabled": true,
"is_debug_enabled": false,
"is_preview_deployments_enabled": true,
"is_git_submodules_enabled": false,
"is_git_lfs_enabled": false,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-02T00:00:00Z"
},
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-02T00:00:00Z"
}
Loading