diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml deleted file mode 100644 index 7373affc..00000000 --- a/.github/workflows/codespell.yml +++ /dev/null @@ -1,22 +0,0 @@ ---- -name: Codespell - -on: - push: - branches: [master] - pull_request: - branches: [master] - -permissions: - contents: read - -jobs: - codespell: - name: Check for spelling errors - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Codespell - uses: codespell-project/actions-codespell@v2 diff --git a/datatype/neuronjson/fields.go b/datatype/neuronjson/fields.go index fcd5be30..3c95bf0d 100644 --- a/datatype/neuronjson/fields.go +++ b/datatype/neuronjson/fields.go @@ -83,42 +83,51 @@ func removeReservedFields(data NeuronJSON, showFields Fields) NeuronJSON { return out } -// Return a subset of fields from the NeuronJSON +// Return a subset of fields from the NeuronJSON where fieldMap is a map of field names to include. func selectFields(data NeuronJSON, fieldMap map[string]struct{}, showUser, showTime bool) NeuronJSON { - out := data.copy() + out := NeuronJSON{} + + // Always include bodyid if it exists. + if v, ok := data["bodyid"]; ok { + out["bodyid"] = v + } + if len(fieldMap) > 0 { - for field := range data { - if field == "bodyid" { + for key := range fieldMap { + // Skip "bodyid", already copied. + if key == "bodyid" { continue } - if _, found := fieldMap[field]; found { - if !showUser { - delete(out, field+"_user") + // Add the primary field if present. + if v, ok := data[key]; ok { + out[key] = v + } + // Optionally include accompanying _user and _time fields. + if showUser { + if v, ok := data[key+"_user"]; ok { + out[key+"_user"] = v } - if !showTime { - delete(out, field+"_time") + } + if showTime { + if v, ok := data[key+"_time"]; ok { + out[key+"_time"] = v } - } else { - delete(out, field) - delete(out, field+"_time") - delete(out, field+"_user") } } } else { - if !showUser { - for field := range data { - if strings.HasSuffix(field, "_user") { - delete(out, field) - } + // No fieldMap provided, copy all keys except suppressed ones. + for key, v := range data { + // If we're not showing user info, skip fields ending in _user. + if !showUser && strings.HasSuffix(key, "_user") { + continue } - } - if !showTime { - for field := range data { - if strings.HasSuffix(field, "_time") { - delete(out, field) - } + // If we're not showing time info, skip fields ending in _time. + if !showTime && strings.HasSuffix(key, "_time") { + continue } + out[key] = v } } + return out } diff --git a/datatype/neuronjson/fields_test.go b/datatype/neuronjson/fields_test.go new file mode 100644 index 00000000..77c479f0 --- /dev/null +++ b/datatype/neuronjson/fields_test.go @@ -0,0 +1,75 @@ +package neuronjson + +import ( + "testing" +) + +func TestSelectFields(t *testing.T) { + data := NeuronJSON{ + "bodyid": 123, + "name": "neuron1", + "name_user": "user1", + "name_time": "2023-01-01T00:00:00Z", + "type": "X1", + "type_user": "user2", + "type_time": "2023-01-02T00:00:00Z", + } + + tests := []struct { + name string + fieldMap map[string]struct{} + showUser bool + showTime bool + expected NeuronJSON + }{ + { + name: "Show all fields", + fieldMap: map[string]struct{}{}, + showUser: true, + showTime: true, + expected: data, + }, + { + name: "Show only bodyid", + fieldMap: map[string]struct{}{"bodyid": {}}, + showUser: true, + showTime: true, + expected: NeuronJSON{"bodyid": 123}, + }, + { + name: "Show name without user and time", + fieldMap: map[string]struct{}{"name": {}}, + showUser: false, + showTime: false, + expected: NeuronJSON{"bodyid": 123, "name": "neuron1"}, + }, + { + name: "Show type with user but not time", + fieldMap: map[string]struct{}{"type": {}}, + showUser: true, + showTime: false, + expected: NeuronJSON{"bodyid": 123, "type": "X1", "type_user": "user2"}, + }, + { + name: "Show type_user but not type", + fieldMap: map[string]struct{}{"type_user": {}}, + showUser: true, + showTime: false, + expected: NeuronJSON{"bodyid": 123, "type_user": "user2"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := selectFields(data, tt.fieldMap, tt.showUser, tt.showTime) + if len(result) != len(tt.expected) { + t.Errorf("expected %v, got %v", tt.expected, result) + } + for k, v := range tt.expected { + if result[k] != v { + t.Errorf("expected %v for key %s, got %v", v, k, result[k]) + } + } + }) + } +} diff --git a/datatype/neuronjson/neuronjson_test.go b/datatype/neuronjson/neuronjson_test.go index b6017b64..e73ff6fc 100644 --- a/datatype/neuronjson/neuronjson_test.go +++ b/datatype/neuronjson/neuronjson_test.go @@ -1383,6 +1383,55 @@ func TestAll(t *testing.T) { } } +func TestAllWithUser(t *testing.T) { + if err := server.OpenTest(); err != nil { + t.Fatalf("can't open test server: %v\n", err) + } + defer server.CloseTest() + + uuid, _ := initTestRepo() + + payload := bytes.NewBufferString(`{"typename": "neuronjson", "dataname": "neurons"}`) + apiStr := fmt.Sprintf("%srepo/%s/instance", server.WebAPIPath, uuid) + server.TestHTTP(t, "POST", apiStr, payload) + + allNeurons := make(ListNeuronJSON, len(testData)) + var keyreq = make([]string, len(testData)) + for i := 0; i < len(testData); i++ { + keyreq[i] = fmt.Sprintf("%snode/%s/neurons/key/%s?u=tester", server.WebAPIPath, uuid, testData[i].key) + server.TestHTTP(t, "POST", keyreq[i], strings.NewReader(testData[i].val)) + if err := json.Unmarshal([]byte(testData[i].val), &(allNeurons[i])); err != nil { + t.Fatalf("Unable to parse test annotation %d: %v\n", i, err) + } + } + + // Get all neuronjson with only baz_user selected as field. + expectedVal := ListNeuronJSON{ + NeuronJSON{ + "bodyid": uint64(1000), + "baz_user": "tester", + }, + NeuronJSON{ + "bodyid": uint64(2000), + "baz_user": "tester", + }, + NeuronJSON{ + "bodyid": uint64(4000), + "baz_user": "tester", + }, + } + allreq := fmt.Sprintf("%snode/%s/neurons/all?show=user&fields=baz_user", server.WebAPIPath, uuid) + returnValue := server.TestHTTP(t, "GET", allreq, nil) + var neurons ListNeuronJSON + if err := json.Unmarshal(returnValue, &neurons); err != nil { + t.Fatalf("Unable to parse return from /all request: %v\n", err) + } + sort.Sort(&neurons) + if !reflect.DeepEqual(neurons, expectedVal) { + t.Fatalf("Response to /all is incorrect. Expected: %v, Got: %v\n", expectedVal, neurons) + } +} + func TestDeleteWithNull(t *testing.T) { if err := server.OpenTest(); err != nil { t.Fatalf("can't open test server: %v\n", err)