Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performer custom fields #5487

Merged
merged 23 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
bc71969
Backend changes
WithoutPants Aug 12, 2024
0216812
Show custom field values
WithoutPants Nov 16, 2024
d6156ab
Add custom fields table input
WithoutPants Nov 16, 2024
2664905
Add custom field filtering
WithoutPants Nov 19, 2024
1aa5e55
Add unit tests and fix bugs
WithoutPants Nov 19, 2024
6a2a98b
Overhaul custom fields input
WithoutPants Nov 20, 2024
d792e94
Disallow JSON objects and arrays for now
WithoutPants Nov 20, 2024
b13045f
Encode numeric values correctly in UI
WithoutPants Nov 20, 2024
9edec83
Handle json numbers in filter
WithoutPants Nov 20, 2024
2aa6e8f
Include custom fields in import/export
WithoutPants Nov 21, 2024
703f5e1
Anonymise performer custom fields
WithoutPants Nov 21, 2024
4c04787
Fix numeric handling in UI
WithoutPants Nov 22, 2024
1b3d01f
Move json.Number handler functions to api
WithoutPants Nov 22, 2024
4c227a9
Handle json.Number conversion in api
WithoutPants Nov 22, 2024
53df908
Fix input format when creating performers
WithoutPants Nov 22, 2024
afe5899
Show empty custom fields
WithoutPants Nov 22, 2024
16ea010
Add validation and unit testing for custom field names
WithoutPants Nov 27, 2024
d510333
Validate field names in UI
WithoutPants Nov 27, 2024
e1e0e85
Overhaul custom field input controls
WithoutPants Nov 27, 2024
8603786
Style long field names
WithoutPants Nov 27, 2024
f0f448f
Format custom field id properly
WithoutPants Nov 29, 2024
1e6ad46
Fix numeric value detection again
WithoutPants Nov 29, 2024
eb653b9
Fix ID sanitisation
WithoutPants Dec 2, 2024
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
8 changes: 8 additions & 0 deletions graphql/schema/types/filters.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ input StashIDCriterionInput {
modifier: CriterionModifier!
}

input CustomFieldCriterionInput {
field: String!
value: [Any!]
modifier: CriterionModifier!
}

input PerformerFilterType {
AND: PerformerFilterType
OR: PerformerFilterType
Expand Down Expand Up @@ -182,6 +188,8 @@ input PerformerFilterType {
created_at: TimestampCriterionInput
"Filter by last update time"
updated_at: TimestampCriterionInput

custom_fields: [CustomFieldCriterionInput!]
}

input SceneMarkerFilterType {
Expand Down
7 changes: 7 additions & 0 deletions graphql/schema/types/metadata.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -338,3 +338,10 @@ type SystemStatus {
input MigrateInput {
backupPath: String!
}

input CustomFieldsInput {
"If populated, the entire custom fields map will be replaced with this value"
full: Map
"If populated, only the keys in this map will be updated"
partial: Map
}
8 changes: 8 additions & 0 deletions graphql/schema/types/performer.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ type Performer {
updated_at: Time!
groups: [Group!]!
movies: [Movie!]! @deprecated(reason: "use groups instead")

custom_fields: Map!
}

input PerformerCreateInput {
Expand Down Expand Up @@ -93,6 +95,8 @@ input PerformerCreateInput {
hair_color: String
weight: Int
ignore_auto_tag: Boolean

custom_fields: Map
}

input PerformerUpdateInput {
Expand Down Expand Up @@ -129,6 +133,8 @@ input PerformerUpdateInput {
hair_color: String
weight: Int
ignore_auto_tag: Boolean

custom_fields: CustomFieldsInput
}

input BulkUpdateStrings {
Expand Down Expand Up @@ -167,6 +173,8 @@ input BulkPerformerUpdateInput {
hair_color: String
weight: Int
ignore_auto_tag: Boolean

custom_fields: CustomFieldsInput
}

input PerformerDestroyInput {
Expand Down
36 changes: 36 additions & 0 deletions internal/api/json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package api

import (
"encoding/json"
"strings"
)

// JSONNumberToNumber converts a JSON number to either a float64 or int64.
func jsonNumberToNumber(n json.Number) interface{} {
if strings.Contains(string(n), ".") {
f, _ := n.Float64()
return f
}
ret, _ := n.Int64()
return ret
}

// ConvertMapJSONNumbers converts all JSON numbers in a map to either float64 or int64.
func convertMapJSONNumbers(m map[string]interface{}) (ret map[string]interface{}) {
if m == nil {
return nil
}

ret = make(map[string]interface{})
for k, v := range m {
if n, ok := v.(json.Number); ok {
ret[k] = jsonNumberToNumber(n)
} else if mm, ok := v.(map[string]interface{}); ok {
ret[k] = convertMapJSONNumbers(mm)
} else {
ret[k] = v
}
}

return ret
}
60 changes: 60 additions & 0 deletions internal/api/json_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package api

import (
"encoding/json"
"testing"

"github.com/stretchr/testify/assert"
)

func TestConvertMapJSONNumbers(t *testing.T) {
tests := []struct {
name string
input map[string]interface{}
expected map[string]interface{}
}{
{
name: "Convert JSON numbers to numbers",
input: map[string]interface{}{
"int": json.Number("12"),
"float": json.Number("12.34"),
"string": "foo",
},
expected: map[string]interface{}{
"int": int64(12),
"float": 12.34,
"string": "foo",
},
},
{
name: "Convert JSON numbers to numbers in nested maps",
input: map[string]interface{}{
"foo": map[string]interface{}{
"int": json.Number("56"),
"float": json.Number("56.78"),
"nested-string": "bar",
},
"int": json.Number("12"),
"float": json.Number("12.34"),
"string": "foo",
},
expected: map[string]interface{}{
"foo": map[string]interface{}{
"int": int64(56),
"float": 56.78,
"nested-string": "bar",
},
"int": int64(12),
"float": 12.34,
"string": "foo",
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := convertMapJSONNumbers(tt.input)
assert.Equal(t, tt.expected, result)
})
}
}
221 changes: 221 additions & 0 deletions internal/api/loaders/customfieldsloader_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading