diff --git a/addon/flamenco/gui.py b/addon/flamenco/gui.py index 0fef1f3d..e518ec13 100644 --- a/addon/flamenco/gui.py +++ b/addon/flamenco/gui.py @@ -51,7 +51,7 @@ class FLAMENCO_PT_job_submission(bpy.types.Panel): ) if not job_types.are_job_types_available(): return - col.prop(context.scene, "flamenco_worker_tag", text="Tag") + col.prop(context.scene, "flamenco_worker_tag", text="Worker Tag") # Job properties: job_col = layout.column(align=True) diff --git a/internal/manager/api_impl/jobs.go b/internal/manager/api_impl/jobs.go index 48ea8fe5..886d594a 100644 --- a/internal/manager/api_impl/jobs.go +++ b/internal/manager/api_impl/jobs.go @@ -687,7 +687,7 @@ func jobDBtoAPI(dbJob *persistence.Job) api.Job { apiJob.DeleteRequestedAt = &dbJob.DeleteRequestedAt.Time } if dbJob.WorkerTag != nil { - apiJob.WorkerTag = &dbJob.WorkerTag.UUID + apiJob.WorkerTag = workerTagDBtoAPI(dbJob.WorkerTag) } return apiJob diff --git a/internal/manager/api_impl/jobs_query_test.go b/internal/manager/api_impl/jobs_query_test.go index 9be480b1..28a9e994 100644 --- a/internal/manager/api_impl/jobs_query_test.go +++ b/internal/manager/api_impl/jobs_query_test.go @@ -89,6 +89,60 @@ func TestQueryJobs(t *testing.T) { assertResponseJSON(t, echoCtx, http.StatusOK, expectedJobs) } +func TestFetchJob(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + mf := newMockedFlamenco(mockCtrl) + + dbJob := persistence.Job{ + UUID: "afc47568-bd9d-4368-8016-e91d945db36d", + Name: "работа", + JobType: "test", + Priority: 50, + Status: api.JobStatusActive, + Settings: persistence.StringInterfaceMap{ + "result": "/render/frames/exploding.kittens", + }, + Metadata: persistence.StringStringMap{ + "project": "/projects/exploding-kittens", + }, + WorkerTag: &persistence.WorkerTag{ + UUID: "d86e1b84-5ee2-4784-a178-65963eeb484b", + Name: "Tikkie terug Kees!", + Description: "", + }, + } + + echoCtx := mf.prepareMockedRequest(nil) + mf.persistence.EXPECT().FetchJob(gomock.Any(), dbJob.UUID).Return(&dbJob, nil) + + require.NoError(t, mf.flamenco.FetchJob(echoCtx, dbJob.UUID)) + + expectedJob := api.Job{ + SubmittedJob: api.SubmittedJob{ + Name: "работа", + Type: "test", + Priority: 50, + Settings: &api.JobSettings{AdditionalProperties: map[string]interface{}{ + "result": "/render/frames/exploding.kittens", + }}, + Metadata: &api.JobMetadata{AdditionalProperties: map[string]string{ + "project": "/projects/exploding-kittens", + }}, + }, + Id: "afc47568-bd9d-4368-8016-e91d945db36d", + Status: api.JobStatusActive, + WorkerTag: &api.WorkerTag{ + Id: ptr("d86e1b84-5ee2-4784-a178-65963eeb484b"), + Name: "Tikkie terug Kees!", + Description: nil, // Empty description should just be excluded from the JSON. + }, + } + + assertResponseJSON(t, echoCtx, http.StatusOK, expectedJob) +} + func TestFetchTask(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() diff --git a/internal/manager/api_impl/jobs_test.go b/internal/manager/api_impl/jobs_test.go index b5134cab..32942cbe 100644 --- a/internal/manager/api_impl/jobs_test.go +++ b/internal/manager/api_impl/jobs_test.go @@ -415,6 +415,11 @@ func TestSubmitJobWithWorkerTag(t *testing.T) { DeleteRequestedAt: nil, Activity: "", Status: api.JobStatusQueued, + WorkerTag: &api.WorkerTag{ + Id: ptr(workerTagUUID), + Name: "first tag", + Description: ptr("my first tag"), + }, }) } diff --git a/internal/manager/api_impl/worker_mgt.go b/internal/manager/api_impl/worker_mgt.go index 88ca016d..564a4a9b 100644 --- a/internal/manager/api_impl/worker_mgt.go +++ b/internal/manager/api_impl/worker_mgt.go @@ -313,8 +313,11 @@ func (f *Flamenco) FetchWorkerTag(e echo.Context, tagUUID string) error { logger.Error().Err(err).Msg("fetching worker tag") return sendAPIError(e, http.StatusInternalServerError, "error fetching worker tag: %v", err) } + if tag == nil { + panic("Could fetch a worker tag without error, but then the returned tag was still nil") + } - return e.JSON(http.StatusOK, workerTagDBtoAPI(*tag)) + return e.JSON(http.StatusOK, workerTagDBtoAPI(tag)) } func (f *Flamenco) UpdateWorkerTag(e echo.Context, tagUUID string) error { @@ -387,8 +390,8 @@ func (f *Flamenco) FetchWorkerTags(e echo.Context) error { apiTags := []api.WorkerTag{} for _, dbTag := range dbTags { - apiTag := workerTagDBtoAPI(*dbTag) - apiTags = append(apiTags, apiTag) + apiTag := workerTagDBtoAPI(dbTag) + apiTags = append(apiTags, *apiTag) } tagList := api.WorkerTagList{ @@ -443,7 +446,7 @@ func (f *Flamenco) CreateWorkerTag(e echo.Context) error { sioUpdate := eventbus.NewWorkerTagUpdate(&dbTag) f.broadcaster.BroadcastNewWorkerTag(sioUpdate) - return e.JSON(http.StatusOK, workerTagDBtoAPI(dbTag)) + return e.JSON(http.StatusOK, workerTagDBtoAPI(&dbTag)) } func workerSummary(w persistence.Worker) api.WorkerSummary { @@ -479,7 +482,7 @@ func workerDBtoAPI(w persistence.Worker) api.Worker { if len(w.Tags) > 0 { tags := []api.WorkerTag{} for i := range w.Tags { - tags = append(tags, workerTagDBtoAPI(*w.Tags[i])) + tags = append(tags, *workerTagDBtoAPI(w.Tags[i])) } apiWorker.Tags = &tags } @@ -487,7 +490,11 @@ func workerDBtoAPI(w persistence.Worker) api.Worker { return apiWorker } -func workerTagDBtoAPI(wc persistence.WorkerTag) api.WorkerTag { +func workerTagDBtoAPI(wc *persistence.WorkerTag) *api.WorkerTag { + if wc == nil { + return nil + } + uuid := wc.UUID // Take a copy for safety. apiTag := api.WorkerTag{ @@ -497,5 +504,5 @@ func workerTagDBtoAPI(wc persistence.WorkerTag) api.WorkerTag { if len(wc.Description) > 0 { apiTag.Description = &wc.Description } - return apiTag + return &apiTag } diff --git a/internal/manager/persistence/jobs.go b/internal/manager/persistence/jobs.go index 8e17e73b..7941da4a 100644 --- a/internal/manager/persistence/jobs.go +++ b/internal/manager/persistence/jobs.go @@ -359,6 +359,18 @@ func (db *DB) FetchJob(ctx context.Context, jobUUID string) (*Job, error) { if err != nil { return nil, err } + + if sqlcJob.WorkerTagID.Valid { + workerTag, err := fetchWorkerTagByID(db.gormDB, uint(sqlcJob.WorkerTagID.Int64)) + switch { + case errors.Is(err, sql.ErrNoRows): + return nil, ErrWorkerTagNotFound + case err != nil: + return nil, workerTagError(err, "fetching worker tag of job") + } + gormJob.WorkerTag = workerTag + } + return &gormJob, nil } diff --git a/internal/manager/persistence/jobs_test.go b/internal/manager/persistence/jobs_test.go index b7d352ee..7a2cb85e 100644 --- a/internal/manager/persistence/jobs_test.go +++ b/internal/manager/persistence/jobs_test.go @@ -75,6 +75,38 @@ func TestStoreAuthoredJobWithShamanCheckoutID(t *testing.T) { assert.Equal(t, job.Storage.ShamanCheckoutID, fetchedJob.Storage.ShamanCheckoutID) } +func TestStoreAuthoredJobWithWorkerTag(t *testing.T) { + ctx, cancel, db := persistenceTestFixtures(1 * time.Second) + defer cancel() + + workerTagUUID := "daa811ac-6861-4004-8748-7700aebc244c" + require.NoError(t, db.CreateWorkerTag(ctx, &WorkerTag{ + UUID: workerTagUUID, + Name: "🐈", + Description: "Mrieuw", + })) + workerTag, err := db.FetchWorkerTag(ctx, workerTagUUID) + require.NoError(t, err) + + job := createTestAuthoredJobWithTasks() + job.WorkerTagUUID = workerTagUUID + + err = db.StoreAuthoredJob(ctx, job) + require.NoError(t, err) + + fetchedJob, err := db.FetchJob(ctx, job.JobID) + require.NoError(t, err) + require.NotNil(t, fetchedJob) + + require.NotNil(t, fetchedJob.WorkerTagID) + assert.Equal(t, *fetchedJob.WorkerTagID, workerTag.ID) + + require.NotNil(t, fetchedJob.WorkerTag) + assert.Equal(t, fetchedJob.WorkerTag.Name, workerTag.Name) + assert.Equal(t, fetchedJob.WorkerTag.Description, workerTag.Description) + assert.Equal(t, fetchedJob.WorkerTag.UUID, workerTagUUID) +} + func TestFetchTaskJobUUID(t *testing.T) { ctx, cancel, db := persistenceTestFixtures(1 * time.Second) defer cancel() diff --git a/internal/manager/persistence/worker_tag.go b/internal/manager/persistence/worker_tag.go index ed318a81..0fdebeec 100644 --- a/internal/manager/persistence/worker_tag.go +++ b/internal/manager/persistence/worker_tag.go @@ -53,6 +53,16 @@ func fetchWorkerTag(gormDB *gorm.DB, uuid string) (*WorkerTag, error) { return &w, nil } +// fetchWorkerTagByID fetches the worker tag using the given database instance. +func fetchWorkerTagByID(gormDB *gorm.DB, id uint) (*WorkerTag, error) { + w := WorkerTag{} + tx := gormDB.First(&w, "id = ?", id) + if tx.Error != nil { + return nil, workerTagError(tx.Error, "fetching worker tag") + } + return &w, nil +} + func (db *DB) SaveWorkerTag(ctx context.Context, tag *WorkerTag) error { if err := db.gormDB.WithContext(ctx).Save(tag).Error; err != nil { return workerTagError(err, "saving worker tag") diff --git a/web/app/src/components/jobs/JobDetails.vue b/web/app/src/components/jobs/JobDetails.vue index 319c6ba4..6b1bd1cd 100644 --- a/web/app/src/components/jobs/JobDetails.vue +++ b/web/app/src/components/jobs/JobDetails.vue @@ -52,9 +52,15 @@ {{ jobData.status }} -