From 4f6b139236ecd494ad2944c1f441b16cf2d81fe8 Mon Sep 17 00:00:00 2001 From: kindlyturnips Date: Thu, 15 Feb 2024 15:46:17 -0600 Subject: [PATCH 1/5] Added tuple delete support for CSV file --- cmd/tuple/delete.go | 13 ++++--------- cmd/tuple/testdata/tuples.csv | 4 ++-- internal/tuplefile/read.go | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/cmd/tuple/delete.go b/cmd/tuple/delete.go index ff5ab8e..3d08182 100644 --- a/cmd/tuple/delete.go +++ b/cmd/tuple/delete.go @@ -19,14 +19,13 @@ package tuple import ( "context" "fmt" - "os" "github.com/openfga/go-sdk/client" "github.com/spf13/cobra" - "gopkg.in/yaml.v3" "github.com/openfga/cli/internal/cmdutils" "github.com/openfga/cli/internal/output" + "github.com/openfga/cli/internal/tuplefile" ) // deleteCmd represents the delete command. @@ -46,19 +45,15 @@ var deleteCmd = &cobra.Command{ if err != nil { return fmt.Errorf("failed to parse file name due to %w", err) } + if fileName != "" { - var tuples []client.ClientTupleKeyWithoutCondition - data, err := os.ReadFile(fileName) + tuplesWithCondition, err := tuplefile.ReadTupleFile(fileName) if err != nil { return fmt.Errorf("failed to read file %s due to %w", fileName, err) } - err = yaml.Unmarshal(data, &tuples) - if err != nil { - return fmt.Errorf("failed to parse input tuples due to %w", err) - } - + var tuples = tuplefile.ClientTupleKeyToTupleKeyWithoutCondition(tuplesWithCondition) maxTuplesPerWrite, err := cmd.Flags().GetInt("max-tuples-per-write") if err != nil { return fmt.Errorf("failed to parse max tuples per write due to %w", err) diff --git a/cmd/tuple/testdata/tuples.csv b/cmd/tuple/testdata/tuples.csv index b2f7810..4e2a621 100644 --- a/cmd/tuple/testdata/tuples.csv +++ b/cmd/tuple/testdata/tuples.csv @@ -1,4 +1,4 @@ user_type,user_id,user_relation,relation,object_type,object_id,condition_name,condition_context -user,anne,,owner,folder,product,inOfficeIP, +user,chris,,owner,folder,product,inOfficeIP, folder,product,,parent,folder,product-2021,inOfficeIP,"{""ip_addr"":""10.0.0.1""}" -team,fga,member,viewer,folder,product-2021,, +team,fga,member,viewer,folder,product-2021,, \ No newline at end of file diff --git a/internal/tuplefile/read.go b/internal/tuplefile/read.go index 041d43c..565d9a3 100644 --- a/internal/tuplefile/read.go +++ b/internal/tuplefile/read.go @@ -5,6 +5,7 @@ import ( "os" "path" + openfga "github.com/openfga/go-sdk" "github.com/openfga/go-sdk/client" "gopkg.in/yaml.v3" ) @@ -32,3 +33,16 @@ func ReadTupleFile(fileName string) ([]client.ClientTupleKey, error) { return tuples, nil } + +func ClientTupleKeyToTupleKeyWithoutCondition(clientTupleKey []client.ClientTupleKey) []openfga.TupleKeyWithoutCondition { + var tuples []openfga.TupleKeyWithoutCondition + for _, tuple := range clientTupleKey { + convertedTuple := openfga.TupleKeyWithoutCondition{ + User: tuple.User, + Relation: tuple.Relation, + Object: tuple.Object, + } + tuples = append(tuples, convertedTuple) + } + return tuples +} From 7e724b5032f25300544807e8e8ea8314a195a896 Mon Sep 17 00:00:00 2001 From: kindlyturnips Date: Thu, 15 Feb 2024 15:46:17 -0600 Subject: [PATCH 2/5] feat: support csv in tuple delete --- cmd/tuple/delete.go | 15 +-- cmd/tuple/delete_test.go | 193 ++++++++++++++++++++++++++++++++++ cmd/tuple/testdata/tuples.csv | 2 +- internal/tuplefile/read.go | 14 +++ 4 files changed, 213 insertions(+), 11 deletions(-) create mode 100644 cmd/tuple/delete_test.go diff --git a/cmd/tuple/delete.go b/cmd/tuple/delete.go index ff5ab8e..3426d56 100644 --- a/cmd/tuple/delete.go +++ b/cmd/tuple/delete.go @@ -19,14 +19,13 @@ package tuple import ( "context" "fmt" - "os" "github.com/openfga/go-sdk/client" "github.com/spf13/cobra" - "gopkg.in/yaml.v3" "github.com/openfga/cli/internal/cmdutils" "github.com/openfga/cli/internal/output" + "github.com/openfga/cli/internal/tuplefile" ) // deleteCmd represents the delete command. @@ -46,19 +45,15 @@ var deleteCmd = &cobra.Command{ if err != nil { return fmt.Errorf("failed to parse file name due to %w", err) } + if fileName != "" { - var tuples []client.ClientTupleKeyWithoutCondition - data, err := os.ReadFile(fileName) + clientTuples, err := tuplefile.ReadTupleFile(fileName) if err != nil { return fmt.Errorf("failed to read file %s due to %w", fileName, err) } - err = yaml.Unmarshal(data, &tuples) - if err != nil { - return fmt.Errorf("failed to parse input tuples due to %w", err) - } - + var openfgaTuples = tuplefile.ClientTupleKeyToTupleKeyWithoutCondition(clientTuples) maxTuplesPerWrite, err := cmd.Flags().GetInt("max-tuples-per-write") if err != nil { return fmt.Errorf("failed to parse max tuples per write due to %w", err) @@ -70,7 +65,7 @@ var deleteCmd = &cobra.Command{ } deleteRequest := client.ClientWriteRequest{ - Deletes: tuples, + Deletes: openfgaTuples, } response, err := ImportTuples(fgaClient, deleteRequest, maxTuplesPerWrite, maxParallelRequests) if err != nil { diff --git a/cmd/tuple/delete_test.go b/cmd/tuple/delete_test.go new file mode 100644 index 0000000..fbba164 --- /dev/null +++ b/cmd/tuple/delete_test.go @@ -0,0 +1,193 @@ +package tuple + +import ( + "testing" + + openfga "github.com/openfga/go-sdk" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/openfga/cli/internal/tuplefile" +) + +func TestDeleteTuplesFileData(t *testing.T) { //nolint:funlen + t.Parallel() + + tests := []struct { + name string + file string + expectedTuples []openfga.TupleKeyWithoutCondition + expectedError string + }{ + { + name: "it can correctly parse a csv file", + file: "testdata/tuples.csv", + expectedTuples: []openfga.TupleKeyWithoutCondition{ + { + User: "user:anne", + Relation: "owner", + Object: "folder:product", + }, + { + User: "folder:product", + Relation: "parent", + Object: "folder:product-2021", + }, + { + User: "team:fga#member", + Relation: "viewer", + Object: "folder:product-2021", + }, + }, + }, + { + name: "it can correctly parse a csv file regardless of columns order", + file: "testdata/tuples_other_columns_order.csv", + expectedTuples: []openfga.TupleKeyWithoutCondition{ + { + User: "user:anne", + Relation: "owner", + Object: "folder:product", + }, + { + User: "folder:product", + Relation: "parent", + Object: "folder:product-2021", + }, + { + User: "team:fga#member", + Relation: "viewer", + Object: "folder:product-2021", + }, + }, + }, + { + name: "it can correctly parse a csv file without optional fields", + file: "testdata/tuples_without_optional_fields.csv", + expectedTuples: []openfga.TupleKeyWithoutCondition{ + { + User: "user:anne", + Relation: "owner", + Object: "folder:product", + }, + { + User: "folder:product", + Relation: "parent", + Object: "folder:product-2021", + }, + }, + }, + { + name: "it can correctly parse a csv file with condition_name header but no condition_context header", + file: "testdata/tuples_with_condition_name_but_no_condition_context.csv", + expectedTuples: []openfga.TupleKeyWithoutCondition{ + { + User: "user:anne", + Relation: "owner", + Object: "folder:product", + }, + { + User: "folder:product", + Relation: "parent", + Object: "folder:product-2021", + }, + { + User: "team:fga#member", + Relation: "viewer", + Object: "folder:product-2021", + }, + }, + }, + { + name: "it can correctly parse a json file", + file: "testdata/tuples.json", + expectedTuples: []openfga.TupleKeyWithoutCondition{ + { + User: "user:anne", + Relation: "owner", + Object: "folder:product", + }, + { + User: "folder:product", + Relation: "parent", + Object: "folder:product-2021", + }, + { + User: "user:beth", + Relation: "viewer", + Object: "folder:product-2021", + }, + }, + }, + { + name: "it can correctly parse a yaml file", + file: "testdata/tuples.yaml", + expectedTuples: []openfga.TupleKeyWithoutCondition{ + { + User: "user:anne", + Relation: "owner", + Object: "folder:product", + }, + { + User: "folder:product", + Relation: "parent", + Object: "folder:product-2021", + }, + { + User: "user:beth", + Relation: "viewer", + Object: "folder:product-2021", + }, + }, + }, + { + name: "it fails to parse a non-supported file format", + file: "testdata/tuples.toml", + expectedError: "failed to parse input tuples: unsupported file format \".toml\"", + }, + { + name: "it fails to parse a csv file with wrong headers", + file: "testdata/tuples_wrong_headers.csv", + expectedError: "failed to parse input tuples: invalid header \"a\", valid headers are user_type,user_id,user_relation,relation,object_type,object_id,condition_name,condition_context", + }, + { + name: "it fails to parse a csv file with missing required headers", + file: "testdata/tuples_missing_required_headers.csv", + expectedError: "failed to parse input tuples: csv header missing (\"object_id\")", + }, + { + name: "it fails to parse a csv file with missing condition_name header when condition_context is present", + file: "testdata/tuples_missing_condition_name_header.csv", + expectedError: "failed to parse input tuples: missing \"condition_name\" header which is required when \"condition_context\" is present", + }, + { + name: "it fails to parse an empty csv file", + file: "testdata/tuples_empty.csv", + expectedError: "failed to parse input tuples: failed to read csv headers: EOF", + }, + { + name: "it fails to parse a csv file with invalid rows", + file: "testdata/tuples_with_invalid_rows.csv", + expectedError: "failed to parse input tuples: failed to read tuple from csv file: record on line 2: wrong number of fields", + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + actualTuples, err := tuplefile.ReadTupleFile(test.file) + deleteTuples := tuplefile.ClientTupleKeyToTupleKeyWithoutCondition(actualTuples) + + if test.expectedError != "" { + require.EqualError(t, err, test.expectedError) + + return + } + + require.NoError(t, err) + assert.Equal(t, test.expectedTuples, deleteTuples) + }) + } +} diff --git a/cmd/tuple/testdata/tuples.csv b/cmd/tuple/testdata/tuples.csv index b2f7810..ad40474 100644 --- a/cmd/tuple/testdata/tuples.csv +++ b/cmd/tuple/testdata/tuples.csv @@ -1,4 +1,4 @@ user_type,user_id,user_relation,relation,object_type,object_id,condition_name,condition_context user,anne,,owner,folder,product,inOfficeIP, folder,product,,parent,folder,product-2021,inOfficeIP,"{""ip_addr"":""10.0.0.1""}" -team,fga,member,viewer,folder,product-2021,, +team,fga,member,viewer,folder,product-2021,, \ No newline at end of file diff --git a/internal/tuplefile/read.go b/internal/tuplefile/read.go index 041d43c..565d9a3 100644 --- a/internal/tuplefile/read.go +++ b/internal/tuplefile/read.go @@ -5,6 +5,7 @@ import ( "os" "path" + openfga "github.com/openfga/go-sdk" "github.com/openfga/go-sdk/client" "gopkg.in/yaml.v3" ) @@ -32,3 +33,16 @@ func ReadTupleFile(fileName string) ([]client.ClientTupleKey, error) { return tuples, nil } + +func ClientTupleKeyToTupleKeyWithoutCondition(clientTupleKey []client.ClientTupleKey) []openfga.TupleKeyWithoutCondition { + var tuples []openfga.TupleKeyWithoutCondition + for _, tuple := range clientTupleKey { + convertedTuple := openfga.TupleKeyWithoutCondition{ + User: tuple.User, + Relation: tuple.Relation, + Object: tuple.Object, + } + tuples = append(tuples, convertedTuple) + } + return tuples +} From 2ecd7390b922d7732c65ded7efa8a67f31f6e2f3 Mon Sep 17 00:00:00 2001 From: kindlyturnips Date: Mon, 19 Feb 2024 10:33:47 -0600 Subject: [PATCH 3/5] feat: support csv in tuple delete --- cli | 1 + cmd/tuple/delete.go | 6 +- cmd/tuple/delete_test.go | 193 ++++++++++++++++++++++++++++++++++ cmd/tuple/testdata/tuples.csv | 2 +- openfga | 1 + 5 files changed, 199 insertions(+), 4 deletions(-) create mode 160000 cli create mode 100644 cmd/tuple/delete_test.go create mode 160000 openfga diff --git a/cli b/cli new file mode 160000 index 0000000..f8d2c52 --- /dev/null +++ b/cli @@ -0,0 +1 @@ +Subproject commit f8d2c52105a4b5f6f477167db657bda90979aa36 diff --git a/cmd/tuple/delete.go b/cmd/tuple/delete.go index 3d08182..3426d56 100644 --- a/cmd/tuple/delete.go +++ b/cmd/tuple/delete.go @@ -48,12 +48,12 @@ var deleteCmd = &cobra.Command{ if fileName != "" { - tuplesWithCondition, err := tuplefile.ReadTupleFile(fileName) + clientTuples, err := tuplefile.ReadTupleFile(fileName) if err != nil { return fmt.Errorf("failed to read file %s due to %w", fileName, err) } - var tuples = tuplefile.ClientTupleKeyToTupleKeyWithoutCondition(tuplesWithCondition) + var openfgaTuples = tuplefile.ClientTupleKeyToTupleKeyWithoutCondition(clientTuples) maxTuplesPerWrite, err := cmd.Flags().GetInt("max-tuples-per-write") if err != nil { return fmt.Errorf("failed to parse max tuples per write due to %w", err) @@ -65,7 +65,7 @@ var deleteCmd = &cobra.Command{ } deleteRequest := client.ClientWriteRequest{ - Deletes: tuples, + Deletes: openfgaTuples, } response, err := ImportTuples(fgaClient, deleteRequest, maxTuplesPerWrite, maxParallelRequests) if err != nil { diff --git a/cmd/tuple/delete_test.go b/cmd/tuple/delete_test.go new file mode 100644 index 0000000..fbba164 --- /dev/null +++ b/cmd/tuple/delete_test.go @@ -0,0 +1,193 @@ +package tuple + +import ( + "testing" + + openfga "github.com/openfga/go-sdk" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/openfga/cli/internal/tuplefile" +) + +func TestDeleteTuplesFileData(t *testing.T) { //nolint:funlen + t.Parallel() + + tests := []struct { + name string + file string + expectedTuples []openfga.TupleKeyWithoutCondition + expectedError string + }{ + { + name: "it can correctly parse a csv file", + file: "testdata/tuples.csv", + expectedTuples: []openfga.TupleKeyWithoutCondition{ + { + User: "user:anne", + Relation: "owner", + Object: "folder:product", + }, + { + User: "folder:product", + Relation: "parent", + Object: "folder:product-2021", + }, + { + User: "team:fga#member", + Relation: "viewer", + Object: "folder:product-2021", + }, + }, + }, + { + name: "it can correctly parse a csv file regardless of columns order", + file: "testdata/tuples_other_columns_order.csv", + expectedTuples: []openfga.TupleKeyWithoutCondition{ + { + User: "user:anne", + Relation: "owner", + Object: "folder:product", + }, + { + User: "folder:product", + Relation: "parent", + Object: "folder:product-2021", + }, + { + User: "team:fga#member", + Relation: "viewer", + Object: "folder:product-2021", + }, + }, + }, + { + name: "it can correctly parse a csv file without optional fields", + file: "testdata/tuples_without_optional_fields.csv", + expectedTuples: []openfga.TupleKeyWithoutCondition{ + { + User: "user:anne", + Relation: "owner", + Object: "folder:product", + }, + { + User: "folder:product", + Relation: "parent", + Object: "folder:product-2021", + }, + }, + }, + { + name: "it can correctly parse a csv file with condition_name header but no condition_context header", + file: "testdata/tuples_with_condition_name_but_no_condition_context.csv", + expectedTuples: []openfga.TupleKeyWithoutCondition{ + { + User: "user:anne", + Relation: "owner", + Object: "folder:product", + }, + { + User: "folder:product", + Relation: "parent", + Object: "folder:product-2021", + }, + { + User: "team:fga#member", + Relation: "viewer", + Object: "folder:product-2021", + }, + }, + }, + { + name: "it can correctly parse a json file", + file: "testdata/tuples.json", + expectedTuples: []openfga.TupleKeyWithoutCondition{ + { + User: "user:anne", + Relation: "owner", + Object: "folder:product", + }, + { + User: "folder:product", + Relation: "parent", + Object: "folder:product-2021", + }, + { + User: "user:beth", + Relation: "viewer", + Object: "folder:product-2021", + }, + }, + }, + { + name: "it can correctly parse a yaml file", + file: "testdata/tuples.yaml", + expectedTuples: []openfga.TupleKeyWithoutCondition{ + { + User: "user:anne", + Relation: "owner", + Object: "folder:product", + }, + { + User: "folder:product", + Relation: "parent", + Object: "folder:product-2021", + }, + { + User: "user:beth", + Relation: "viewer", + Object: "folder:product-2021", + }, + }, + }, + { + name: "it fails to parse a non-supported file format", + file: "testdata/tuples.toml", + expectedError: "failed to parse input tuples: unsupported file format \".toml\"", + }, + { + name: "it fails to parse a csv file with wrong headers", + file: "testdata/tuples_wrong_headers.csv", + expectedError: "failed to parse input tuples: invalid header \"a\", valid headers are user_type,user_id,user_relation,relation,object_type,object_id,condition_name,condition_context", + }, + { + name: "it fails to parse a csv file with missing required headers", + file: "testdata/tuples_missing_required_headers.csv", + expectedError: "failed to parse input tuples: csv header missing (\"object_id\")", + }, + { + name: "it fails to parse a csv file with missing condition_name header when condition_context is present", + file: "testdata/tuples_missing_condition_name_header.csv", + expectedError: "failed to parse input tuples: missing \"condition_name\" header which is required when \"condition_context\" is present", + }, + { + name: "it fails to parse an empty csv file", + file: "testdata/tuples_empty.csv", + expectedError: "failed to parse input tuples: failed to read csv headers: EOF", + }, + { + name: "it fails to parse a csv file with invalid rows", + file: "testdata/tuples_with_invalid_rows.csv", + expectedError: "failed to parse input tuples: failed to read tuple from csv file: record on line 2: wrong number of fields", + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + actualTuples, err := tuplefile.ReadTupleFile(test.file) + deleteTuples := tuplefile.ClientTupleKeyToTupleKeyWithoutCondition(actualTuples) + + if test.expectedError != "" { + require.EqualError(t, err, test.expectedError) + + return + } + + require.NoError(t, err) + assert.Equal(t, test.expectedTuples, deleteTuples) + }) + } +} diff --git a/cmd/tuple/testdata/tuples.csv b/cmd/tuple/testdata/tuples.csv index 4e2a621..ad40474 100644 --- a/cmd/tuple/testdata/tuples.csv +++ b/cmd/tuple/testdata/tuples.csv @@ -1,4 +1,4 @@ user_type,user_id,user_relation,relation,object_type,object_id,condition_name,condition_context -user,chris,,owner,folder,product,inOfficeIP, +user,anne,,owner,folder,product,inOfficeIP, folder,product,,parent,folder,product-2021,inOfficeIP,"{""ip_addr"":""10.0.0.1""}" team,fga,member,viewer,folder,product-2021,, \ No newline at end of file diff --git a/openfga b/openfga new file mode 160000 index 0000000..a6e0009 --- /dev/null +++ b/openfga @@ -0,0 +1 @@ +Subproject commit a6e000905c3947355f138fe459ae9c48536823f4 From 8903adad5890e66f533cdefed886d84b7e48f062 Mon Sep 17 00:00:00 2001 From: kindlyturnips Date: Mon, 11 Mar 2024 14:29:59 -0500 Subject: [PATCH 4/5] feat: support csv in tuple delete --- cli | 1 - openfga | 1 - 2 files changed, 2 deletions(-) delete mode 160000 cli delete mode 160000 openfga diff --git a/cli b/cli deleted file mode 160000 index f8d2c52..0000000 --- a/cli +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f8d2c52105a4b5f6f477167db657bda90979aa36 diff --git a/openfga b/openfga deleted file mode 160000 index a6e0009..0000000 --- a/openfga +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a6e000905c3947355f138fe459ae9c48536823f4 From d56e0b0ffcca791cf54a02a5f89dd1115d1944f4 Mon Sep 17 00:00:00 2001 From: kindlyturnips Date: Thu, 15 Feb 2024 15:46:17 -0600 Subject: [PATCH 5/5] feat: support csv in tuple delete --- cli | 1 - cmd/tuple/delete.go | 2 +- cmd/tuple/delete_test.go | 21 ++++++++++++--------- internal/tuplefile/read.go | 7 +++++-- openfga | 1 - 5 files changed, 18 insertions(+), 14 deletions(-) delete mode 160000 cli delete mode 160000 openfga diff --git a/cli b/cli deleted file mode 160000 index f8d2c52..0000000 --- a/cli +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f8d2c52105a4b5f6f477167db657bda90979aa36 diff --git a/cmd/tuple/delete.go b/cmd/tuple/delete.go index 3426d56..b7fd75a 100644 --- a/cmd/tuple/delete.go +++ b/cmd/tuple/delete.go @@ -53,7 +53,7 @@ var deleteCmd = &cobra.Command{ return fmt.Errorf("failed to read file %s due to %w", fileName, err) } - var openfgaTuples = tuplefile.ClientTupleKeyToTupleKeyWithoutCondition(clientTuples) + openfgaTuples := tuplefile.ClientTupleKeyToTupleKeyWithoutCondition(clientTuples) maxTuplesPerWrite, err := cmd.Flags().GetInt("max-tuples-per-write") if err != nil { return fmt.Errorf("failed to parse max tuples per write due to %w", err) diff --git a/cmd/tuple/delete_test.go b/cmd/tuple/delete_test.go index fbba164..960d0a6 100644 --- a/cmd/tuple/delete_test.go +++ b/cmd/tuple/delete_test.go @@ -146,9 +146,10 @@ func TestDeleteTuplesFileData(t *testing.T) { //nolint:funlen expectedError: "failed to parse input tuples: unsupported file format \".toml\"", }, { - name: "it fails to parse a csv file with wrong headers", - file: "testdata/tuples_wrong_headers.csv", - expectedError: "failed to parse input tuples: invalid header \"a\", valid headers are user_type,user_id,user_relation,relation,object_type,object_id,condition_name,condition_context", + name: "it fails to parse a csv file with wrong headers", + file: "testdata/tuples_wrong_headers.csv", + expectedError: "failed to parse input tuples: invalid header \"a\", valid headers are " + + "user_type,user_id,user_relation,relation,object_type,object_id,condition_name,condition_context", }, { name: "it fails to parse a csv file with missing required headers", @@ -156,9 +157,10 @@ func TestDeleteTuplesFileData(t *testing.T) { //nolint:funlen expectedError: "failed to parse input tuples: csv header missing (\"object_id\")", }, { - name: "it fails to parse a csv file with missing condition_name header when condition_context is present", - file: "testdata/tuples_missing_condition_name_header.csv", - expectedError: "failed to parse input tuples: missing \"condition_name\" header which is required when \"condition_context\" is present", + name: "it fails to parse a csv file with missing condition_name header when condition_context is present", + file: "testdata/tuples_missing_condition_name_header.csv", + expectedError: "failed to parse input tuples: missing \"condition_name\"" + + " header which is required when \"condition_context\" is present", }, { name: "it fails to parse an empty csv file", @@ -166,9 +168,10 @@ func TestDeleteTuplesFileData(t *testing.T) { //nolint:funlen expectedError: "failed to parse input tuples: failed to read csv headers: EOF", }, { - name: "it fails to parse a csv file with invalid rows", - file: "testdata/tuples_with_invalid_rows.csv", - expectedError: "failed to parse input tuples: failed to read tuple from csv file: record on line 2: wrong number of fields", + name: "it fails to parse a csv file with invalid rows", + file: "testdata/tuples_with_invalid_rows.csv", + expectedError: "failed to parse input tuples: failed to read tuple from csv file:" + + " record on line 2: wrong number of fields", }, } diff --git a/internal/tuplefile/read.go b/internal/tuplefile/read.go index 565d9a3..803412e 100644 --- a/internal/tuplefile/read.go +++ b/internal/tuplefile/read.go @@ -34,8 +34,10 @@ func ReadTupleFile(fileName string) ([]client.ClientTupleKey, error) { return tuples, nil } -func ClientTupleKeyToTupleKeyWithoutCondition(clientTupleKey []client.ClientTupleKey) []openfga.TupleKeyWithoutCondition { - var tuples []openfga.TupleKeyWithoutCondition +func ClientTupleKeyToTupleKeyWithoutCondition(clientTupleKey []client.ClientTupleKey, +) []openfga.TupleKeyWithoutCondition { + tuples := make([]openfga.TupleKeyWithoutCondition, 0, len(clientTupleKey)) + for _, tuple := range clientTupleKey { convertedTuple := openfga.TupleKeyWithoutCondition{ User: tuple.User, @@ -44,5 +46,6 @@ func ClientTupleKeyToTupleKeyWithoutCondition(clientTupleKey []client.ClientTupl } tuples = append(tuples, convertedTuple) } + return tuples } diff --git a/openfga b/openfga deleted file mode 160000 index a6e0009..0000000 --- a/openfga +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a6e000905c3947355f138fe459ae9c48536823f4