mirror of
https://github.com/StarFleetCPTN/GoMFT.git
synced 2026-10-14 18:18:15 +02:00
feat: Enhance configuration handling and UI for job management
- Update .gitignore to include new provider files and ensure proper tracking. - Refactor job and config forms to support multiple configuration selections, improving user experience. - Implement logic to handle the initialization of skipProcessedFiles with a default value. - Add new provider form templates for better organization and management of source and destination configurations. - Enhance tests for provider forms and job configurations to ensure robust functionality. - Introduce nullable handling for skipProcessedFiles in the database schema and update related migrations. - Improve error handling and validation in job creation and editing processes.
This commit is contained in:
1 parent
d6fa0c1603
commit
558e81c7e8
33 files changed
+2018
-776
No files matched your search
+17
-1
@@ -104,7 +104,7 @@ type TransferConfig struct {
|
||||
ArchiveEnabled bool `gorm:"default:false" form:"archive_enabled"`
|
||||
RcloneFlags string `form:"rclone_flags"`
|
||||
DeleteAfterTransfer bool `gorm:"default:false" form:"delete_after_transfer"`
|
||||
SkipProcessedFiles bool `gorm:"default:true" form:"skip_processed_files"`
|
||||
SkipProcessedFiles *bool `gorm:"default:true" form:"skip_processed_files"`
|
||||
MaxConcurrentTransfers int `gorm:"default:4" form:"max_concurrent_transfers"` // Number of concurrent file transfers
|
||||
CreatedBy uint
|
||||
User User `gorm:"foreignkey:CreatedBy"`
|
||||
@@ -831,6 +831,9 @@ func (db *DB) GenerateRcloneConfig(config *TransferConfig) error {
|
||||
}
|
||||
|
||||
func (db *DB) GetActiveJobs() ([]Job, error) {
|
||||
if db.DB == nil {
|
||||
return nil, fmt.Errorf("database connection is nil")
|
||||
}
|
||||
var jobs []Job
|
||||
err := db.Preload("Config").Where("enabled = ?", true).Find(&jobs).Error
|
||||
return jobs, err
|
||||
@@ -862,3 +865,16 @@ func (db *DB) GetConfigsForJob(jobID uint) ([]TransferConfig, error) {
|
||||
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
// GetSkipProcessedFiles returns the value of SkipProcessedFiles with a default if nil
|
||||
func (tc *TransferConfig) GetSkipProcessedFiles() bool {
|
||||
if tc.SkipProcessedFiles == nil {
|
||||
return true // Default to true if not set
|
||||
}
|
||||
return *tc.SkipProcessedFiles
|
||||
}
|
||||
|
||||
// SetSkipProcessedFiles sets the SkipProcessedFiles field
|
||||
func (tc *TransferConfig) SetSkipProcessedFiles(value bool) {
|
||||
tc.SkipProcessedFiles = &value
|
||||
}
|
||||
@@ -303,6 +303,137 @@ func TestJobCRUD(t *testing.T) {
|
||||
assert.Error(t, err, "Getting deleted job should return an error")
|
||||
}
|
||||
|
||||
// Helper function to test if a config ID is selected for a job
|
||||
func configSelected(job *Job, configID uint) bool {
|
||||
// Check if the job has the config ID in its list
|
||||
for _, id := range job.GetConfigIDsList() {
|
||||
if id == configID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
// As a fallback, check the primary ConfigID
|
||||
return job.ConfigID == configID
|
||||
}
|
||||
|
||||
func TestJobMultipleConfigs(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
|
||||
// Create a test user
|
||||
testUser := &User{
|
||||
Email: fmt.Sprintf("test-multi-%d@example.com", time.Now().UnixNano()),
|
||||
PasswordHash: "hashed_password",
|
||||
LastPasswordChange: time.Now(),
|
||||
}
|
||||
err := db.CreateUser(testUser)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create user: %v", err)
|
||||
}
|
||||
|
||||
// Create multiple test configs
|
||||
config1 := &TransferConfig{
|
||||
Name: "Test Config 1",
|
||||
SourceType: "local",
|
||||
SourcePath: "/source/path1",
|
||||
DestinationType: "local",
|
||||
DestinationPath: "/destination/path1",
|
||||
CreatedBy: testUser.ID,
|
||||
}
|
||||
err = db.CreateTransferConfig(config1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
config2 := &TransferConfig{
|
||||
Name: "Test Config 2",
|
||||
SourceType: "local",
|
||||
SourcePath: "/source/path2",
|
||||
DestinationType: "local",
|
||||
DestinationPath: "/destination/path2",
|
||||
CreatedBy: testUser.ID,
|
||||
}
|
||||
err = db.CreateTransferConfig(config2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
config3 := &TransferConfig{
|
||||
Name: "Test Config 3",
|
||||
SourceType: "local",
|
||||
SourcePath: "/source/path3",
|
||||
DestinationType: "local",
|
||||
DestinationPath: "/destination/path3",
|
||||
CreatedBy: testUser.ID,
|
||||
}
|
||||
err = db.CreateTransferConfig(config3)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test 1: Create job with multiple configs
|
||||
testJob := &Job{
|
||||
Name: "Multi Config Job",
|
||||
Schedule: "0 * * * *",
|
||||
Enabled: true,
|
||||
CreatedBy: testUser.ID,
|
||||
}
|
||||
|
||||
// Set multiple config IDs
|
||||
configIDs := []uint{config1.ID, config2.ID, config3.ID}
|
||||
testJob.SetConfigIDsList(configIDs)
|
||||
|
||||
// Verify ConfigIDs string format
|
||||
assert.Contains(t, testJob.ConfigIDs, fmt.Sprintf("%d", config1.ID))
|
||||
assert.Contains(t, testJob.ConfigIDs, fmt.Sprintf("%d", config2.ID))
|
||||
assert.Contains(t, testJob.ConfigIDs, fmt.Sprintf("%d", config3.ID))
|
||||
|
||||
// Verify ConfigID is set to the first config
|
||||
assert.Equal(t, config1.ID, testJob.ConfigID)
|
||||
|
||||
// Save the job
|
||||
err = db.CreateJob(testJob)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test 2: Retrieve job and check config IDs
|
||||
retrievedJob, err := db.GetJob(testJob.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify retrieved config IDs
|
||||
retrievedIDs := retrievedJob.GetConfigIDsList()
|
||||
assert.Len(t, retrievedIDs, 3)
|
||||
assert.Contains(t, retrievedIDs, config1.ID)
|
||||
assert.Contains(t, retrievedIDs, config2.ID)
|
||||
assert.Contains(t, retrievedIDs, config3.ID)
|
||||
|
||||
// Test 3: Test configSelected function
|
||||
assert.True(t, configSelected(retrievedJob, config1.ID))
|
||||
assert.True(t, configSelected(retrievedJob, config2.ID))
|
||||
assert.True(t, configSelected(retrievedJob, config3.ID))
|
||||
assert.False(t, configSelected(retrievedJob, uint(999)))
|
||||
|
||||
// Test 4: Get configs for job
|
||||
configs, err := db.GetConfigsForJob(testJob.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, configs, 3)
|
||||
|
||||
// Verify config names are correct
|
||||
configNames := make([]string, len(configs))
|
||||
for i, config := range configs {
|
||||
configNames[i] = config.Name
|
||||
}
|
||||
assert.Contains(t, configNames, "Test Config 1")
|
||||
assert.Contains(t, configNames, "Test Config 2")
|
||||
assert.Contains(t, configNames, "Test Config 3")
|
||||
|
||||
// Test 5: Update config IDs
|
||||
updatedIDs := []uint{config1.ID, config3.ID} // Remove config2
|
||||
retrievedJob.SetConfigIDsList(updatedIDs)
|
||||
err = db.UpdateJob(retrievedJob)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify update
|
||||
updatedJob, err := db.GetJob(testJob.ID)
|
||||
assert.NoError(t, err)
|
||||
updatedRetrievedIDs := updatedJob.GetConfigIDsList()
|
||||
assert.Len(t, updatedRetrievedIDs, 2)
|
||||
assert.Contains(t, updatedRetrievedIDs, config1.ID)
|
||||
assert.Contains(t, updatedRetrievedIDs, config3.ID)
|
||||
assert.NotContains(t, updatedRetrievedIDs, config2.ID)
|
||||
}
|
||||
|
||||
func TestJobHistoryCRUD(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ func InitMigrations(db *gorm.DB) *gormigrate.Gormigrate {
|
||||
AddSkipProcessedFilesColumn(),
|
||||
AddMaxConcurrentTransfersColumn(),
|
||||
AddMultiConfigSupport(),
|
||||
UpdateSkipProcessedFilesToNullable(),
|
||||
}
|
||||
|
||||
return gormigrate.New(db, gormigrate.DefaultOptions, migrations)
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// UpdateSkipProcessedFilesToNullable changes the skip_processed_files column to be nullable
|
||||
func UpdateSkipProcessedFilesToNullable() *gormigrate.Migration {
|
||||
return &gormigrate.Migration{
|
||||
ID: "20250515_update_skip_processed_files_to_nullable",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
// SQLite specific command - this would need to be adjusted for other databases
|
||||
return tx.Exec("ALTER TABLE transfer_configs RENAME TO transfer_configs_old; " +
|
||||
"CREATE TABLE transfer_configs (" +
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
||||
"name VARCHAR(255) NOT NULL, " +
|
||||
"source_type VARCHAR(255) NOT NULL, " +
|
||||
"source_path VARCHAR(255) NOT NULL, " +
|
||||
"source_host VARCHAR(255), " +
|
||||
"source_port INTEGER DEFAULT 22, " +
|
||||
"source_user VARCHAR(255), " +
|
||||
"source_key_file VARCHAR(255), " +
|
||||
"source_bucket VARCHAR(255), " +
|
||||
"source_region VARCHAR(255), " +
|
||||
"source_access_key VARCHAR(255), " +
|
||||
"source_endpoint VARCHAR(255), " +
|
||||
"source_share VARCHAR(255), " +
|
||||
"source_domain VARCHAR(255), " +
|
||||
"source_passive_mode BOOLEAN DEFAULT true, " +
|
||||
"source_client_id VARCHAR(255), " +
|
||||
"source_drive_id VARCHAR(255), " +
|
||||
"source_team_drive VARCHAR(255), " +
|
||||
"file_pattern VARCHAR(255) DEFAULT '*', " +
|
||||
"output_pattern VARCHAR(255), " +
|
||||
"destination_type VARCHAR(255) NOT NULL, " +
|
||||
"destination_path VARCHAR(255) NOT NULL, " +
|
||||
"dest_host VARCHAR(255), " +
|
||||
"dest_port INTEGER DEFAULT 22, " +
|
||||
"dest_user VARCHAR(255), " +
|
||||
"dest_key_file VARCHAR(255), " +
|
||||
"dest_bucket VARCHAR(255), " +
|
||||
"dest_region VARCHAR(255), " +
|
||||
"dest_access_key VARCHAR(255), " +
|
||||
"dest_endpoint VARCHAR(255), " +
|
||||
"dest_share VARCHAR(255), " +
|
||||
"dest_domain VARCHAR(255), " +
|
||||
"dest_passive_mode BOOLEAN DEFAULT true, " +
|
||||
"dest_client_id VARCHAR(255), " +
|
||||
"dest_drive_id VARCHAR(255), " +
|
||||
"dest_team_drive VARCHAR(255), " +
|
||||
"archive_path VARCHAR(255), " +
|
||||
"archive_enabled BOOLEAN DEFAULT false, " +
|
||||
"rclone_flags VARCHAR(255), " +
|
||||
"delete_after_transfer BOOLEAN DEFAULT false, " +
|
||||
"skip_processed_files BOOLEAN DEFAULT true, " + // Keep as BOOLEAN, but now it's nullable
|
||||
"max_concurrent_transfers INTEGER DEFAULT 4, " +
|
||||
"created_by INTEGER, " +
|
||||
"created_at DATETIME, " +
|
||||
"updated_at DATETIME" +
|
||||
"); " +
|
||||
"INSERT INTO transfer_configs SELECT * FROM transfer_configs_old; " +
|
||||
"DROP TABLE transfer_configs_old;").Error
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
// No need to rollback as the data structure remains compatible
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user