diff --git a/backend/app/api/handlers/v1/v1_ctrl_actions.go b/backend/app/api/handlers/v1/v1_ctrl_actions.go index b9c51725..643abf29 100644 --- a/backend/app/api/handlers/v1/v1_ctrl_actions.go +++ b/backend/app/api/handlers/v1/v1_ctrl_actions.go @@ -147,7 +147,7 @@ func (ctrl *V1Controller) HandleWipeInventory() errchain.HandlerFunc { // Publish mutation events for wiped resources if ctrl.bus != nil { if options.WipeTags { - ctrl.bus.Publish(eventbus.EventLabelMutation, eventbus.GroupMutationEvent{GID: ctx.GID}) + ctrl.bus.Publish(eventbus.EventTagMutation, eventbus.GroupMutationEvent{GID: ctx.GID}) } if options.WipeLocations { ctrl.bus.Publish(eventbus.EventLocationMutation, eventbus.GroupMutationEvent{GID: ctx.GID}) diff --git a/backend/internal/core/services/reporting/io_row.go b/backend/internal/core/services/reporting/io_row.go index 3ebb2aa5..51897a04 100644 --- a/backend/internal/core/services/reporting/io_row.go +++ b/backend/internal/core/services/reporting/io_row.go @@ -15,7 +15,7 @@ type ExportItemFields struct { type ExportCSVRow struct { ImportRef string `csv:"HB.import_ref"` Location LocationString `csv:"HB.location"` - TagStr TagString `csv:"HB.tags"` + TagStr TagString `csv:"HB.tags|HB.labels"` AssetID repo.AssetID `csv:"HB.asset_id"` Archived bool `csv:"HB.archived"` URL string `csv:"HB.url"` diff --git a/backend/internal/core/services/reporting/io_sheet.go b/backend/internal/core/services/reporting/io_sheet.go index 8549a175..0f364278 100644 --- a/backend/internal/core/services/reporting/io_sheet.go +++ b/backend/internal/core/services/reporting/io_sheet.go @@ -41,6 +41,54 @@ func (s *IOSheet) indexHeaders() { } } +// primaryCSVTag returns the primary header name from a csv struct tag which +// may contain alternatives separated by '|'. For example: "HB.tags|HB.labels" +// will return "HB.tags". +func primaryCSVTag(tag string) string { + if tag == "" { + return "" + } + if i := strings.Index(tag, "|"); i >= 0 { + return strings.TrimSpace(tag[:i]) + } + return tag +} + +// csvTagAlternatives splits a tag with alternatives separated by '|'. +func csvTagAlternatives(tag string) []string { + if tag == "" { + return nil + } + parts := strings.Split(tag, "|") + for i := range parts { + parts[i] = strings.TrimSpace(parts[i]) + } + return parts +} + +// findColumnForTag tries to find a column index for a csv tag. The tag may +// contain multiple alternatives separated by '|'. It will return the first +// matching column index it finds. +func (s *IOSheet) findColumnForTag(tag string) (int, bool) { + if s.index == nil { + s.indexHeaders() + } + + // Try primary tag first and then alternatives. + alts := csvTagAlternatives(tag) + if len(alts) == 0 { + return s.GetColumn(tag) + } + + for _, t := range alts { + if col, ok := s.GetColumn(t); ok { + return col, true + } + } + + return 0, false +} + func (s *IOSheet) GetColumn(str string) (col int, ok bool) { if s.index == nil { s.indexHeaders() @@ -88,7 +136,7 @@ func (s *IOSheet) Read(data io.Reader) error { continue } - col, ok := s.GetColumn(tag) + col, ok := s.findColumnForTag(tag) if !ok { continue } @@ -238,7 +286,7 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, gid uuid. st := reflect.TypeOf(ExportCSVRow{}) - // Write headers + // Write headers (use the primary tag when alternatives are provided) for i := 0; i < st.NumField(); i++ { field := st.Field(i) tag := field.Tag.Get("csv") @@ -246,7 +294,7 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, gid uuid. continue } - s.headers = append(s.headers, tag) + s.headers = append(s.headers, primaryCSVTag(tag)) } for _, h := range customHeaders { @@ -285,7 +333,7 @@ func (s *IOSheet) CSV() ([][]string, error) { continue } - col, ok := s.GetColumn(tag) + col, ok := s.findColumnForTag(tag) if !ok { continue } diff --git a/frontend/composables/use-server-events.ts b/frontend/composables/use-server-events.ts index 3a197195..8ac11eec 100644 --- a/frontend/composables/use-server-events.ts +++ b/frontend/composables/use-server-events.ts @@ -1,7 +1,7 @@ export enum ServerEvent { LocationMutation = "location.mutation", ItemMutation = "item.mutation", - TagMutation = "tags.mutation", + TagMutation = "tag.mutation", } export type EventMessage = { diff --git a/frontend/lib/api/__test__/user/labels.test.ts b/frontend/lib/api/__test__/user/tags.test.ts similarity index 97% rename from frontend/lib/api/__test__/user/labels.test.ts rename to frontend/lib/api/__test__/user/tags.test.ts index ac22c011..8c2fa3bf 100644 --- a/frontend/lib/api/__test__/user/labels.test.ts +++ b/frontend/lib/api/__test__/user/tags.test.ts @@ -4,7 +4,7 @@ import type { UserClient } from "../../user"; import { factories } from "../factories"; import { sharedUserClient } from "../test-utils"; -describe("locations lifecycle (create, update, delete)", () => { +describe("tags lifecycle (create, update, delete)", () => { /** * useTag sets up a tag resource for testing, and returns a function * that can be used to delete the tag from the backend server.