From 4b8a5475a6324ba9830e83e1d62b6b7c0a055997 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Mon, 25 Nov 2024 16:04:07 -0500 Subject: [PATCH] Omitempty --- Makefile | 1 + pkg/database/models.go | 168 ++++++++++++++++++------------------ util/omitempty/omitempty.go | 98 +++++++++++++++++++++ 3 files changed, 183 insertions(+), 84 deletions(-) create mode 100644 util/omitempty/omitempty.go diff --git a/Makefile b/Makefile index d70c2a8..f4a4faa 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,7 @@ generate: sqlc generate -f sql/sqlc.yaml protoc -I=pkg/pb/ --go_out=pkg/ pkg/pb/stillbox.proto go generate ./... + go run ./util/omitempty/omitempty.go lint: golangci-lint run diff --git a/pkg/database/models.go b/pkg/database/models.go index cc46a39..042b84b 100644 --- a/pkg/database/models.go +++ b/pkg/database/models.go @@ -14,115 +14,115 @@ import ( ) type Alert struct { - ID int `json:"id"` - Time pgtype.Timestamptz `json:"time"` - TGID int `json:"tgid"` - SystemID int `json:"system_id"` - Weight *float32 `json:"weight"` - Score *float32 `json:"score"` - OrigScore *float32 `json:"orig_score"` - Notified bool `json:"notified"` - Metadata []byte `json:"metadata"` + ID int `json:"id,omitempty"` + Time pgtype.Timestamptz `json:"time,omitempty"` + TGID int `json:"tgid,omitempty"` + SystemID int `json:"system_id,omitempty"` + Weight *float32 `json:"weight,omitempty"` + Score *float32 `json:"score,omitempty"` + OrigScore *float32 `json:"orig_score,omitempty"` + Notified bool `json:"notified,omitempty"` + Metadata []byte `json:"metadata,omitempty"` } type ApiKey struct { - ID int `json:"id"` - Owner int `json:"owner"` - CreatedAt time.Time `json:"created_at"` - Expires pgtype.Timestamp `json:"expires"` - Disabled *bool `json:"disabled"` - ApiKey string `json:"api_key"` + ID int `json:"id,omitempty"` + Owner int `json:"owner,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty"` + Expires pgtype.Timestamp `json:"expires,omitempty"` + Disabled *bool `json:"disabled,omitempty"` + ApiKey string `json:"api_key,omitempty"` } type Call struct { - ID uuid.UUID `json:"id"` - Submitter *int32 `json:"submitter"` - System int `json:"system"` - Talkgroup int `json:"talkgroup"` - CallDate pgtype.Timestamptz `json:"call_date"` - AudioName *string `json:"audio_name"` - AudioBlob []byte `json:"audio_blob"` - Duration *int32 `json:"duration"` - AudioType *string `json:"audio_type"` - AudioUrl *string `json:"audio_url"` - Frequency int `json:"frequency"` - Frequencies []int `json:"frequencies"` - Patches []int `json:"patches"` - TGLabel *string `json:"tg_label"` - TGAlphaTag *string `json:"tg_alpha_tag"` - TGGroup *string `json:"tg_group"` - Source int `json:"source"` - Transcript *string `json:"transcript"` + ID uuid.UUID `json:"id,omitempty"` + Submitter *int32 `json:"submitter,omitempty"` + System int `json:"system,omitempty"` + Talkgroup int `json:"talkgroup,omitempty"` + CallDate pgtype.Timestamptz `json:"call_date,omitempty"` + AudioName *string `json:"audio_name,omitempty"` + AudioBlob []byte `json:"audio_blob,omitempty"` + Duration *int32 `json:"duration,omitempty"` + AudioType *string `json:"audio_type,omitempty"` + AudioUrl *string `json:"audio_url,omitempty"` + Frequency int `json:"frequency,omitempty"` + Frequencies []int `json:"frequencies,omitempty"` + Patches []int `json:"patches,omitempty"` + TGLabel *string `json:"tg_label,omitempty"` + TGAlphaTag *string `json:"tg_alpha_tag,omitempty"` + TGGroup *string `json:"tg_group,omitempty"` + Source int `json:"source,omitempty"` + Transcript *string `json:"transcript,omitempty"` } type Incident struct { - ID uuid.UUID `json:"id"` - Name string `json:"name"` - Description *string `json:"description"` - StartTime pgtype.Timestamp `json:"start_time"` - EndTime pgtype.Timestamp `json:"end_time"` - Location []byte `json:"location"` - Metadata []byte `json:"metadata"` + ID uuid.UUID `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + StartTime pgtype.Timestamp `json:"start_time,omitempty"` + EndTime pgtype.Timestamp `json:"end_time,omitempty"` + Location []byte `json:"location,omitempty"` + Metadata []byte `json:"metadata,omitempty"` } type IncidentsCall struct { - IncidentID uuid.UUID `json:"incident_id"` - CallID uuid.UUID `json:"call_id"` - Notes []byte `json:"notes"` + IncidentID uuid.UUID `json:"incident_id,omitempty"` + CallID uuid.UUID `json:"call_id,omitempty"` + Notes []byte `json:"notes,omitempty"` } type Setting struct { - Name string `json:"name"` - UpdatedBy *int32 `json:"updated_by"` - Value []byte `json:"value"` + Name string `json:"name,omitempty"` + UpdatedBy *int32 `json:"updated_by,omitempty"` + Value []byte `json:"value,omitempty"` } type System struct { - ID int `json:"id"` - Name string `json:"name"` + ID int `json:"id,omitempty"` + Name string `json:"name,omitempty"` } type Talkgroup struct { - ID int `json:"id"` - SystemID int32 `json:"system_id"` - TGID int32 `json:"tgid"` - Name *string `json:"name"` - AlphaTag *string `json:"alpha_tag"` - TGGroup *string `json:"tg_group"` - Frequency *int32 `json:"frequency"` - Metadata jsontypes.Metadata `json:"metadata"` - Tags []string `json:"tags"` - Alert bool `json:"alert"` - AlertConfig rules.AlertRules `json:"alert_config"` - Weight float32 `json:"weight"` - Learned bool `json:"learned"` - Ignored bool `json:"ignored"` + ID int `json:"id,omitempty"` + SystemID int32 `json:"system_id,omitempty"` + TGID int32 `json:"tgid,omitempty"` + Name *string `json:"name,omitempty"` + AlphaTag *string `json:"alpha_tag,omitempty"` + TGGroup *string `json:"tg_group,omitempty"` + Frequency *int32 `json:"frequency,omitempty"` + Metadata jsontypes.Metadata `json:"metadata,omitempty"` + Tags []string `json:"tags,omitempty"` + Alert bool `json:"alert,omitempty"` + AlertConfig rules.AlertRules `json:"alert_config,omitempty"` + Weight float32 `json:"weight,omitempty"` + Learned bool `json:"learned,omitempty"` + Ignored bool `json:"ignored,omitempty"` } type TalkgroupVersion struct { - ID int `json:"id"` - Time pgtype.Timestamptz `json:"time"` - CreatedBy *int32 `json:"created_by"` - SystemID *int32 `json:"system_id"` - TGID *int32 `json:"tgid"` - Name *string `json:"name"` - AlphaTag *string `json:"alpha_tag"` - TGGroup *string `json:"tg_group"` - Frequency *int32 `json:"frequency"` - Metadata []byte `json:"metadata"` - Tags []string `json:"tags"` - Alert *bool `json:"alert"` - AlertConfig []byte `json:"alert_config"` - Weight *float32 `json:"weight"` - Learned *bool `json:"learned"` - Ignored *bool `json:"ignored"` + ID int `json:"id,omitempty"` + Time pgtype.Timestamptz `json:"time,omitempty"` + CreatedBy *int32 `json:"created_by,omitempty"` + SystemID *int32 `json:"system_id,omitempty"` + TGID *int32 `json:"tgid,omitempty"` + Name *string `json:"name,omitempty"` + AlphaTag *string `json:"alpha_tag,omitempty"` + TGGroup *string `json:"tg_group,omitempty"` + Frequency *int32 `json:"frequency,omitempty"` + Metadata []byte `json:"metadata,omitempty"` + Tags []string `json:"tags,omitempty"` + Alert *bool `json:"alert,omitempty"` + AlertConfig []byte `json:"alert_config,omitempty"` + Weight *float32 `json:"weight,omitempty"` + Learned *bool `json:"learned,omitempty"` + Ignored *bool `json:"ignored,omitempty"` } type User struct { - ID int `json:"id"` - Username string `json:"username"` - Password string `json:"password"` - Email string `json:"email"` - IsAdmin bool `json:"is_admin"` - Prefs []byte `json:"prefs"` + ID int `json:"id,omitempty"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Email string `json:"email,omitempty"` + IsAdmin bool `json:"is_admin,omitempty"` + Prefs []byte `json:"prefs,omitempty"` } diff --git a/util/omitempty/omitempty.go b/util/omitempty/omitempty.go new file mode 100644 index 0000000..325d526 --- /dev/null +++ b/util/omitempty/omitempty.go @@ -0,0 +1,98 @@ +// This is here until https://github.com/sqlc-dev/sqlc/pull/3117 is merged. +package main + +import ( + "bytes" + "go/ast" + "go/format" + "go/parser" + "go/token" + "log" + "os" + "strings" +) + +const filePath = "./pkg/database/models.go" + +func main() { + // Parse the source code + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, filePath, nil, parser.ParseComments) + if err != nil { + log.Fatal(err) + } + + // Modify the AST + ast.Inspect(f, func(n ast.Node) bool { + switch x := n.(type) { + case *ast.StructType: + for _, field := range x.Fields.List { + if field.Tag == nil { + continue + } + if field.Tag.Value == "" || field.Tag.Kind != token.STRING { + continue + } + + field.Tag.Value = modifyJSONTag(field.Tag.Value) + } + } + return true + }) + + // Write the output back to the original file + var buf bytes.Buffer + err = format.Node(&buf, fset, f) + if err != nil { + log.Fatal(err) + } + outputFile, err := os.Create(filePath) + if err != nil { + log.Fatal(err) + } + defer outputFile.Close() + _, err = outputFile.Write(buf.Bytes()) + if err != nil { + log.Fatal(err) + } +} + +func modifyJSONTag(tagValue string) string { + tagValue = strings.Trim(tagValue, "`") + + tags := strings.Split(tagValue, " ") + var modifiedTags []string + + for _, tag := range tags { + // Only modify JSON tags, leave others as they are. + if !strings.HasPrefix(tag, "json:") { + modifiedTags = append(modifiedTags, tag) + continue + } + + jsonQuoted := tag[5:] // Remove "json:" prefix + jsonValue := strings.Trim(jsonQuoted, "\"") // Remove quotes + jsonOptions := strings.Split(jsonValue, ",") // Split options + + // Check if "omitempty" is already present + hasOmitempty := false + for _, opt := range jsonOptions { + if opt == "omitempty" { + hasOmitempty = true + break + } + } + + // Add "omitempty" if not present and the field is not ignored + if !hasOmitempty && jsonOptions[0] != "-" { + jsonOptions = append(jsonOptions, "omitempty") + } + + // Reconstruct the JSON tag + newJSONTag := "json:\"" + strings.Join(jsonOptions, ",") + "\"" + modifiedTags = append(modifiedTags, newJSONTag) + } + + // Reconstruct the full tag + return "`" + strings.Join(modifiedTags, " ") + "`" +}