diff --git a/.golangci.yaml b/.golangci.yaml index f3fcc79..e9a010d 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -49,6 +49,7 @@ linters-settings: - google.golang.org/protobuf/encoding/protojson - google.golang.org/protobuf/types/known/structpb - gopkg.in/yaml.v3 + - github.com/hashicorp/go-multierror - github.com/gocarina/gocsv test: files: diff --git a/CHANGELOG.md b/CHANGELOG.md index 9208b1a..23a85ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,15 @@ # Changelog +## v0.3.0-beta.1 + +### [0.3.0-beta.1](https://github.com/openfga/cli/compare/v0.2.6...v0.3.0-beta.1) (2024-03-13) + +Added: +- Support for [modular models](https://github.com/openfga/rfcs/blob/main/20231212-modular-models.md) ([#262](https://github.com/openfga/cli/issues/262)) + ## v0.2.7 -### [0.2.7](https://github.com/openfga/cli/compare/v0.2.6...v0.2.7) (2024-04-27) +### [0.2.7](https://github.com/openfga/cli/compare/v0.2.6...v0.2.7) (2024-03-13) Added: - Support for exporting tuples as CSV (#250) - thanks @edwin-Marrima diff --git a/README.md b/README.md index 732a101..b1d34fc 100644 --- a/README.md +++ b/README.md @@ -181,8 +181,8 @@ fga store **create** ###### Parameters * `--name`: Name of the store to be created. If the `model` parameter is specified, the model file name will be used as the default store name. -* `--model`: File with the authorization model. Can be in JSON or OpenFGA format (optional). -* `--format` : Authorization model input format. Can be "fga" or "json" (optional, defaults to the model file extension if present). +* `--model`: File with the authorization model. Can be in JSON, OpenFGA format, or fga.mod file (optional). +* `--format` : Authorization model input format. Can be "fga", "json", or "modular" (optional, defaults to the model file extension if present). ###### Example `fga store create --name "FGA Demo Store"` @@ -352,7 +352,7 @@ fga model **write** ###### Parameters * `--store-id`: Specifies the store id * `--file`: File containing the authorization model. -* `--format`: Authorization model input format. Can be "fga" or "json". Defaults to the file extension if provided (optional) +* `--format`: Authorization model input format. Can be "fga", "json", or "modular". Defaults to the file extension if provided (optional) ###### Example * `fga model write --store-id=01H0H015178Y2V4CX10C2KGHF4 --file model.fga` @@ -423,7 +423,7 @@ fga model **validate** ###### Parameters * `--file`: File containing the authorization model. -* `--format`: Authorization model input format. Can be "fga" or "json". Defaults to the file extension if provided (optional) +* `--format`: Authorization model input format. Can be "fga", "json", or "modular". Defaults to the file extension if provided (optional) ###### Example `fga model validate --file model.json` @@ -555,7 +555,7 @@ fga model **transform** ###### Parameters * `--file`: File containing the authorization model -* `--input-format`: Authorization model input format. Can be "fga" or "json". Defaults to the file extension if provided (optional) +* `--input-format`: Authorization model input format. Can be "fga", "json", or "modular". Defaults to the file extension if provided (optional) ###### Example `fga model transform --file model.json` diff --git a/cmd/model/get.go b/cmd/model/get.go index c8c9d72..dfb6459 100644 --- a/cmd/model/get.go +++ b/cmd/model/get.go @@ -95,7 +95,7 @@ var getCmd = &cobra.Command{ } if getOutputFormat == authorizationmodel.ModelFormatJSON { - return output.Display(authModel.DisplayAsJSON(fields)) //nolint:wrapcheck + return output.Display(authModel.DisplayAsJSON(fields)) } dslModel, err := authModel.DisplayAsDSL(fields) diff --git a/cmd/model/list.go b/cmd/model/list.go index 76ee619..0ca7248 100644 --- a/cmd/model/list.go +++ b/cmd/model/list.go @@ -101,7 +101,7 @@ var listCmd = &cobra.Command{ models.AuthorizationModels = append(models.AuthorizationModels, authModel.DisplayAsJSON(fields)) } - return output.Display(models) //nolint:wrapcheck + return output.Display(models) }, } diff --git a/cmd/model/transform.go b/cmd/model/transform.go index 7b23fda..b56ca8a 100644 --- a/cmd/model/transform.go +++ b/cmd/model/transform.go @@ -32,7 +32,8 @@ var transformCmd = &cobra.Command{ Short: "Transforms an authorization model", Example: `fga model transform --file=model.json fga model transform --file=model.fga -fga model transform '{ "schema_version": "1.1", "type_definitions":[{"type":"user"}] }' --input-format json`, +fga model transform '{ "schema_version": "1.1", "type_definitions":[{"type":"user"}] }' --input-format json +fga model transform --file=fga.mod`, Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { @@ -53,7 +54,8 @@ fga model transform '{ "schema_version": "1.1", "type_definitions":[{"type":"use return err //nolint:wrapcheck } - if transformInputFormat == authorizationmodel.ModelFormatJSON { + if transformInputFormat == authorizationmodel.ModelFormatJSON || + transformInputFormat == authorizationmodel.ModelFormatModular { dslModel, err := authModel.DisplayAsDSL([]string{"model"}) if err != nil { return fmt.Errorf("failed to transform model due to %w", err) @@ -67,13 +69,13 @@ fga model transform '{ "schema_version": "1.1", "type_definitions":[{"type":"use return err //nolint:wrapcheck } - return output.Display(authModel.DisplayAsJSON([]string{"model"})) //nolint:wrapcheck + return output.Display(authModel.DisplayAsJSON([]string{"model"})) }, } var transformInputFormat = authorizationmodel.ModelFormatDefault func init() { - transformCmd.Flags().String("file", "", "File Name. The file should have the model in the JSON or DSL format") - transformCmd.Flags().Var(&transformInputFormat, "input-format", `Authorization model input format. Can be "fga" or "json"`) //nolint:lll + transformCmd.Flags().String("file", "", "File Name. The file should have the model in the JSON or DSL format or be an `fga.mod` format") //nolint:lll + transformCmd.Flags().Var(&transformInputFormat, "input-format", `Authorization model input format. Can be "fga", "json", or "modular"`) //nolint:lll } diff --git a/cmd/model/validate.go b/cmd/model/validate.go index 983b4fe..936654e 100644 --- a/cmd/model/validate.go +++ b/cmd/model/validate.go @@ -108,27 +108,21 @@ var validateCmd = &cobra.Command{ } authModel := authorizationmodel.AuthzModel{} - var err error - - if validateInputFormat == authorizationmodel.ModelFormatJSON { - err = authModel.ReadFromJSONString(inputModel) - } else { - err = authModel.ReadFromDSLString(inputModel) - } + err := authModel.ReadModelFromString(inputModel, validateInputFormat) if err != nil { return err //nolint:wrapcheck } response := validate(authModel) - return output.Display(response) //nolint:wrapcheck + return output.Display(response) }, } var validateInputFormat = authorizationmodel.ModelFormatDefault func init() { - validateCmd.Flags().String("file", "", "File Name. The file should have the model in the JSON or DSL format") - validateCmd.Flags().Var(&validateInputFormat, "format", `Authorization model input format. Can be "fga" or "json"`) + validateCmd.Flags().String("file", "", "File Name. The file should have the model in the JSON or DSL format or be an fga.mod file") //nolint:lll + validateCmd.Flags().Var(&validateInputFormat, "format", `Authorization model input format. Can be "fga", "json", or "modular"`) //nolint:lll } diff --git a/cmd/model/write.go b/cmd/model/write.go index 87d6d78..6e0df1c 100644 --- a/cmd/model/write.go +++ b/cmd/model/write.go @@ -78,12 +78,7 @@ fga model write --store-id=01H0H015178Y2V4CX10C2KGHF4 '{"type_definitions":[{"ty authModel := authorizationmodel.AuthzModel{} - if writeInputFormat == authorizationmodel.ModelFormatJSON { - err = authModel.ReadFromJSONString(inputModel) - } else { - err = authModel.ReadFromDSLString(inputModel) - } - + err = authModel.ReadModelFromString(inputModel, writeInputFormat) if err != nil { return err //nolint:wrapcheck } @@ -93,7 +88,7 @@ fga model write --store-id=01H0H015178Y2V4CX10C2KGHF4 '{"type_definitions":[{"ty return err } - return output.Display(*response) //nolint:wrapcheck + return output.Display(*response) }, } diff --git a/cmd/query/check.go b/cmd/query/check.go index 8957e2f..9a8706f 100644 --- a/cmd/query/check.go +++ b/cmd/query/check.go @@ -81,7 +81,7 @@ var checkCmd = &cobra.Command{ return fmt.Errorf("failed to check due to %w", err) } - return output.Display(*response) //nolint:wrapcheck + return output.Display(*response) }, } diff --git a/cmd/query/expand.go b/cmd/query/expand.go index b08d74d..9901400 100644 --- a/cmd/query/expand.go +++ b/cmd/query/expand.go @@ -61,7 +61,7 @@ var expandCmd = &cobra.Command{ return err } - return output.Display(*response) //nolint:wrapcheck + return output.Display(*response) }, } diff --git a/cmd/query/list-objects.go b/cmd/query/list-objects.go index 9209a2e..3231ea7 100644 --- a/cmd/query/list-objects.go +++ b/cmd/query/list-objects.go @@ -83,7 +83,7 @@ var listObjectsCmd = &cobra.Command{ return fmt.Errorf("failed to list objects due to %w", err) } - return output.Display(*response) //nolint:wrapcheck + return output.Display(*response) }, } diff --git a/cmd/query/list-relations.go b/cmd/query/list-relations.go index 04db95b..8dd384f 100644 --- a/cmd/query/list-relations.go +++ b/cmd/query/list-relations.go @@ -147,7 +147,7 @@ var listRelationsCmd = &cobra.Command{ return fmt.Errorf("failed to list relations due to %w", err) } - return output.Display(*response) //nolint:wrapcheck + return output.Display(*response) }, } diff --git a/cmd/store/create.go b/cmd/store/create.go index 65cde72..4e0b0ca 100644 --- a/cmd/store/create.go +++ b/cmd/store/create.go @@ -74,12 +74,8 @@ func CreateStoreWithModel( if inputModel != "" { authModel := authorizationmodel.AuthzModel{} - if inputFormat == authorizationmodel.ModelFormatJSON { - err = authModel.ReadFromJSONString(inputModel) - } else { - err = authModel.ReadFromDSLString(inputModel) - } + err = authModel.ReadModelFromString(inputModel, inputFormat) if err != nil { return nil, err //nolint:wrapcheck } @@ -127,7 +123,7 @@ export FGA_STORE_ID=$(fga store create --model Model.fga | jq -r .store.id) return err } - return output.Display(response) //nolint:wrapcheck + return output.Display(response) }, } @@ -136,5 +132,5 @@ var createModelInputFormat = authorizationmodel.ModelFormatDefault func init() { createCmd.Flags().String("name", "", "Store Name") createCmd.Flags().String("model", "", "Authorization Model File Name") - createCmd.Flags().Var(&createModelInputFormat, "format", `Authorization model input format. Can be "fga" or "json"`) + createCmd.Flags().Var(&createModelInputFormat, "format", `Authorization model input format. Can be "fga", "json" or "modular`) //nolint:lll } diff --git a/cmd/store/delete.go b/cmd/store/delete.go index ecfda64..40b0885 100644 --- a/cmd/store/delete.go +++ b/cmd/store/delete.go @@ -51,7 +51,7 @@ var deleteCmd = &cobra.Command{ Message string } - return output.Display(returnMessage{Message: "Delete store cancelled"}) //nolint:wrapcheck + return output.Display(returnMessage{Message: "Delete store cancelled"}) } } @@ -64,7 +64,7 @@ var deleteCmd = &cobra.Command{ return fmt.Errorf("failed to delete store %v due to %w", clientConfig.StoreID, err) } - return output.Display(output.EmptyStruct{}) //nolint:wrapcheck + return output.Display(output.EmptyStruct{}) }, } diff --git a/cmd/store/get.go b/cmd/store/get.go index 538b891..fffdca6 100644 --- a/cmd/store/get.go +++ b/cmd/store/get.go @@ -56,7 +56,7 @@ var getCmd = &cobra.Command{ return err } - return output.Display(*response) //nolint:wrapcheck + return output.Display(*response) }, } diff --git a/cmd/store/import.go b/cmd/store/import.go index c56f91d..2b0fbe8 100644 --- a/cmd/store/import.go +++ b/cmd/store/import.go @@ -43,7 +43,7 @@ func importStore( maxParallelRequests int, ) error { var err error - if storeID == "" { //nolint:nestif + if storeID == "" { createStoreAndModelResponse, err := CreateStoreWithModel(clientConfig, storeData.Name, storeData.Model, format) if err != nil { return err @@ -53,12 +53,7 @@ func importStore( authModel := authorizationmodel.AuthzModel{} clientConfig.StoreID = storeID - if format == authorizationmodel.ModelFormatJSON { - err = authModel.ReadFromJSONString(storeData.Model) - } else { - err = authModel.ReadFromDSLString(storeData.Model) - } - + err = authModel.ReadModelFromString(storeData.Model, format) if err != nil { return err //nolint:wrapcheck } @@ -130,7 +125,7 @@ var importCmd = &cobra.Command{ return err } - return output.Display(output.EmptyStruct{}) //nolint:wrapcheck + return output.Display(output.EmptyStruct{}) }, } diff --git a/cmd/store/list.go b/cmd/store/list.go index 8904178..0306412 100644 --- a/cmd/store/list.go +++ b/cmd/store/list.go @@ -82,7 +82,7 @@ var listCmd = &cobra.Command{ return err } - return output.Display(*response) //nolint:wrapcheck + return output.Display(*response) }, } diff --git a/cmd/tuple/changes.go b/cmd/tuple/changes.go index 4236d08..1a3fab0 100644 --- a/cmd/tuple/changes.go +++ b/cmd/tuple/changes.go @@ -99,7 +99,7 @@ var changesCmd = &cobra.Command{ return err } - return output.Display(*response) //nolint:wrapcheck + return output.Display(*response) }, } diff --git a/cmd/tuple/delete.go b/cmd/tuple/delete.go index ff5ab8e..3ac06cc 100644 --- a/cmd/tuple/delete.go +++ b/cmd/tuple/delete.go @@ -77,7 +77,7 @@ var deleteCmd = &cobra.Command{ return err } - return output.Display(*response) //nolint:wrapcheck + return output.Display(*response) } body := &client.ClientDeleteTuplesBody{ client.ClientTupleKeyWithoutCondition{ @@ -92,7 +92,7 @@ var deleteCmd = &cobra.Command{ return fmt.Errorf("failed to delete tuples due to %w", err) } - return output.Display(output.EmptyStruct{}) //nolint:wrapcheck + return output.Display(output.EmptyStruct{}) }, } diff --git a/cmd/tuple/import.go b/cmd/tuple/import.go index 10c88aa..d7424d1 100644 --- a/cmd/tuple/import.go +++ b/cmd/tuple/import.go @@ -182,7 +182,7 @@ var importCmd = &cobra.Command{ return err } - return output.Display(*result) //nolint:wrapcheck + return output.Display(*result) }, } diff --git a/cmd/tuple/read.go b/cmd/tuple/read.go index f15240a..db5b855 100644 --- a/cmd/tuple/read.go +++ b/cmd/tuple/read.go @@ -197,7 +197,7 @@ var readCmd = &cobra.Command{ data = response.simple } - return dataPrinter.Display(data) //nolint:wrapcheck + return dataPrinter.Display(data) }, } diff --git a/go.mod b/go.mod index 2c4a02e..0615de6 100644 --- a/go.mod +++ b/go.mod @@ -5,15 +5,16 @@ go 1.21.8 require ( github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a github.com/golang/mock v1.6.0 + github.com/hashicorp/go-multierror v1.1.1 github.com/mattn/go-isatty v0.0.20 github.com/muesli/mango-cobra v1.2.0 github.com/muesli/roff v0.1.0 github.com/nwidger/jsoncolor v0.3.2 github.com/oklog/ulid/v2 v2.1.0 - github.com/openfga/api/proto v0.0.0-20240201160513-05de9d8be3ee - github.com/openfga/go-sdk v0.3.5 - github.com/openfga/language/pkg/go v0.0.0-20240311093347-a2bba7a149ff - github.com/openfga/openfga v1.5.0 + github.com/openfga/api/proto v0.0.0-20240318145204-66b9e5cb403c + github.com/openfga/go-sdk v0.3.6-0.20240313140700-3de2c059df44 + github.com/openfga/language/pkg/go v0.0.0-20240327204426-18b0254b2be1 + github.com/openfga/openfga v1.5.1 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.18.2 @@ -36,10 +37,9 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/google/cel-go v0.20.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect - github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/karlseguin/ccache/v3 v3.0.5 // indirect diff --git a/go.sum b/go.sum index 521ddbd..85f9ea8 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,17 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= +github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -16,6 +24,12 @@ github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0= +github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= +github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -23,8 +37,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v25.0.3+incompatible h1:D5fy/lYmY7bvZa0XTZ5/UJPljor41F+vdyJG5luQLfQ= -github.com/docker/docker v25.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v25.0.4+incompatible h1:XITZTrq+52tZyZxUOtFIahUf3aH367FLxJzt9vZeAF8= +github.com/docker/docker v25.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -51,8 +65,10 @@ github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= -github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-sql-driver/mysql v1.8.1-0.20240317050433-65395d853d2c h1:oCLNJYPr2xNTnHhZNMMKlLZa8nJl1GaITjrPe4NAiHg= +github.com/go-sql-driver/mysql v1.8.1-0.20240317050433-65395d853d2c/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a h1:RYfmiM0zluBJOiPDJseKLEN4BapJ42uSi9SZBQ2YyiA= github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI= @@ -78,8 +94,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1 h1:HcUWd006luQPljE73d5sk+/VgYPGUReEVz2y1/qylwY= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1/go.mod h1:w9Y7gY31krpLmrVU5ZPG9H7l9fZuRu5/3R3S3FMtVQ4= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -99,8 +115,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.5.3 h1:Ces6/M3wbDXYpM8JyyPD57ivTtJACFZJd885pdIaV2s= -github.com/jackc/pgx/v5 v5.5.3/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww= @@ -109,6 +125,8 @@ github.com/karlseguin/ccache/v3 v3.0.5 h1:hFX25+fxzNjsRlREYsoGNa2LoVEw5mPF8wkWq/ github.com/karlseguin/ccache/v3 v3.0.5/go.mod h1:qxC372+Qn+IBj8Pe3KvGjHPj0sWwEF7AeZVhsNPZ6uY= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= +github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -121,6 +139,8 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -135,6 +155,16 @@ github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6B github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= +github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/muesli/mango v0.2.0 h1:iNNc0c5VLQ6fsMgAqGQofByNUBH2Q2nEbD6TaI+5yyQ= github.com/muesli/mango v0.2.0/go.mod h1:5XFpbC8jY5UUv89YQciiXNlbi+iJgt29VDC5xbzrLL4= github.com/muesli/mango-cobra v1.2.0 h1:DQvjzAM0PMZr85Iv9LIMaYISpTOliMEg+uMFtNbYvWg= @@ -151,16 +181,16 @@ github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU= github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc6 h1:XDqvyKsJEbRtATzkgItUqBA7QHk58yxX1Ov9HERHNqU= -github.com/opencontainers/image-spec v1.1.0-rc6/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= -github.com/openfga/api/proto v0.0.0-20240201160513-05de9d8be3ee h1:ZqeLB0dxo4XgRLRpaF9dDt3S3BShbrGE9NO6L2eUDDE= -github.com/openfga/api/proto v0.0.0-20240201160513-05de9d8be3ee/go.mod h1:XF/4W9je/FGBZQ2M5pbQnrzdKF4VcEEtds3ole9sW5E= -github.com/openfga/go-sdk v0.3.5 h1:KQXhMREh+g/K7HNuZ/YmXuHkREkq0VMKteua4bYr3Uw= -github.com/openfga/go-sdk v0.3.5/go.mod h1:u1iErzj5E9/bhe+8nsMv0gigcYbJtImcdgcE5DmpbBg= -github.com/openfga/language/pkg/go v0.0.0-20240311093347-a2bba7a149ff h1:SE4oI8sZZkOT4+LTy7SZLtXD6J6IiW7YUma8Lw0DTVE= -github.com/openfga/language/pkg/go v0.0.0-20240311093347-a2bba7a149ff/go.mod h1:uuXSPj7C3ImG5UF3rAupq+aC8mZQ3pbR52GRU/DXjLU= -github.com/openfga/openfga v1.5.0 h1:SfQIN3MnmOx8hCrDKi/uMnKeF4y8gYwUA7lmB/O2Jxk= -github.com/openfga/openfga v1.5.0/go.mod h1:ht3w17zJEx1Zzo+iaAwxF+BtwnZAmUDwpJWw5yxrDQU= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/openfga/api/proto v0.0.0-20240318145204-66b9e5cb403c h1:2UmuAlvXoZGQRuZ4gzl0CQGLB+rSke2uo+OXlw9/1t8= +github.com/openfga/api/proto v0.0.0-20240318145204-66b9e5cb403c/go.mod h1:5LtWOArDX4FlbcfDvBoJAzDEYJKLz/OEUoi+0S2tyM8= +github.com/openfga/go-sdk v0.3.6-0.20240313140700-3de2c059df44 h1:JyFxbUvKEqMnCoJL4QoH70kpBxkFaHtmo4tFNKIsfoo= +github.com/openfga/go-sdk v0.3.6-0.20240313140700-3de2c059df44/go.mod h1:dybCHDtJDwkmtlxxtgQrHR2c8HPVOwN493drbXv46Ec= +github.com/openfga/language/pkg/go v0.0.0-20240327204426-18b0254b2be1 h1:Grz0XV79GoTWX1PWzOuGB7ahvF5xZ6Ngh74qDQf4vls= +github.com/openfga/language/pkg/go v0.0.0-20240327204426-18b0254b2be1/go.mod h1:iwtNOC/ypBBmN4ND4JqtLdgskIEN67GRP6HuTVa0dKE= +github.com/openfga/openfga v1.5.1 h1:u76SHaRUFOvzKAYMbtNMc+Z55FunGbDyS8aApsu21dM= +github.com/openfga/openfga v1.5.1/go.mod h1:NR0dvko5ary1fo5Z0VWjX6jWJ6KXRsHD/eEh8bJ4HNA= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= @@ -171,8 +201,10 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pressly/goose/v3 v3.18.0 h1:CUQKjZ0li91GLrMekHPR0yz4UyjT21AqyhSm/ERcPTo= -github.com/pressly/goose/v3 v3.18.0/go.mod h1:NTDry9taDJXEV6IqkABnZqm1MRGOSrCWrNEz1x6f4wI= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/pressly/goose/v3 v3.19.2 h1:z1yuD41jS4iaqLkyjkzGkKBz4rgyz/BYtCyMMGHlgzQ= +github.com/pressly/goose/v3 v3.19.2/go.mod h1:BHkf3LzSBmO8E5FTMPupUYIpMTIh/ZuQVy+YTfhZLD4= github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -191,7 +223,13 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sethvargo/go-retry v0.2.4 h1:T+jHEQy/zKJf5s95UkguisicE0zuF9y7+/vgz08Ocec= github.com/sethvargo/go-retry v0.2.4/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw= +github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= +github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= @@ -222,9 +260,21 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/testcontainers/testcontainers-go v0.29.1 h1:z8kxdFlovA2y97RWx98v/TQ+tR+SXZm6p35M+xB92zk= +github.com/testcontainers/testcontainers-go v0.29.1/go.mod h1:SnKnKQav8UcgtKqjp/AD8bE1MqZm+3TDb/B8crE3XnI= +github.com/testcontainers/testcontainers-go/modules/mysql v0.29.1 h1:SnJtZNcskgxOMyVAT7M+MQjpveP59nwKzlBw2ItX+C8= +github.com/testcontainers/testcontainers-go/modules/mysql v0.29.1/go.mod h1:VhA5dV+O19sx3Y9u9bfO+fbJfP3E7RiMq0nDMEGjslw= +github.com/testcontainers/testcontainers-go/modules/postgres v0.29.1 h1:hTn3MzhR9w4btwfzr/NborGCaeNZG0MPBpufeDj10KA= +github.com/testcontainers/testcontainers-go/modules/postgres v0.29.1/go.mod h1:YsWyy+pHDgvGdi0axGOx6CGXWsE6eqSaApyd1FYYSSc= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= @@ -268,8 +318,8 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/internal/authorizationmodel/format.go b/internal/authorizationmodel/format.go index 986e170..964ee06 100644 --- a/internal/authorizationmodel/format.go +++ b/internal/authorizationmodel/format.go @@ -28,6 +28,7 @@ const ( ModelFormatDefault ModelFormat = "default" ModelFormatJSON ModelFormat = "json" ModelFormatFGA ModelFormat = "fga" + ModelFormatModular ModelFormat = "modular" ) func (format *ModelFormat) String() string { @@ -36,7 +37,7 @@ func (format *ModelFormat) String() string { func (format *ModelFormat) Set(v string) error { switch v { - case "json", "fga": + case "json", "fga", "modular": *format = ModelFormat(v) return nil diff --git a/internal/authorizationmodel/model.go b/internal/authorizationmodel/model.go index a04a4ce..040239a 100644 --- a/internal/authorizationmodel/model.go +++ b/internal/authorizationmodel/model.go @@ -19,8 +19,11 @@ package authorizationmodel import ( "encoding/json" "fmt" + "os" + "path" "time" + "github.com/hashicorp/go-multierror" "github.com/oklog/ulid/v2" pb "github.com/openfga/api/proto/openfga/v1" openfga "github.com/openfga/go-sdk" @@ -190,6 +193,66 @@ func (model *AuthzModel) ReadFromDSLString(dslString string) error { return nil } +func (model *AuthzModel) ReadModelFromModFGA(modFile string) error { + modFileContents, err := os.ReadFile(modFile) + if err != nil { + return fmt.Errorf("failed to read fga.mod file due to %w", err) + } + + parsedModFile, err := language.TransformModFile(string(modFileContents)) + if err != nil { + return fmt.Errorf("failed to transform fga.mod file due to %w", err) + } + + moduleFiles := []language.ModuleFile{} + fileReadErrors := multierror.Error{} + directory := path.Dir(modFile) + + for _, fileName := range parsedModFile.Contents.Value { + filePath := path.Join(directory, fileName.Value) + + fileContents, err := os.ReadFile(filePath) + if err != nil { + fileReadErrors = *multierror.Append( + &fileReadErrors, + fmt.Errorf("failed to read module file %s due to %w", fileName.Value, err), + ) + + continue + } + + moduleFiles = append(moduleFiles, language.ModuleFile{ + Name: fileName.Value, + Contents: string(fileContents), + }) + } + + if len(fileReadErrors.Errors) != 0 { + return &fileReadErrors + } + + parsedAuthModel, err := language.TransformModuleFilesToModel(moduleFiles, parsedModFile.Schema.Value) + if err != nil { + return fmt.Errorf("failed to transform module to model due to %w", err) + } + + bytes, err := protojson.Marshal(parsedAuthModel) + if err != nil { + return fmt.Errorf("failed to transform due to %w", err) + } + + jsonAuthModel := openfga.AuthorizationModel{} + + err = json.Unmarshal(bytes, &jsonAuthModel) + if err != nil { + return fmt.Errorf("failed to transform due to %w", err) + } + + model.Set(jsonAuthModel) + + return nil +} + func (model *AuthzModel) ReadModelFromString(input string, format ModelFormat) error { if input == "" { return nil @@ -202,12 +265,17 @@ func (model *AuthzModel) ReadModelFromString(input string, format ModelFormat) e } return nil - case ModelFormatFGA: - case ModelFormatDefault: + case ModelFormatFGA, ModelFormatDefault: if err := model.ReadFromDSLString(input); err != nil { return err } + return nil + case ModelFormatModular: + if err := model.ReadModelFromModFGA(input); err != nil { + return err + } + return nil } @@ -295,7 +363,7 @@ func (model *AuthzModel) DisplayAsDSL(fields []string) (*string, error) { return nil, fmt.Errorf("unable to unmarshal model json string due to: %w", err) } - transformedJSON, err := language.TransformJSONProtoToDSL(&modelPb) + transformedJSON, err := language.TransformJSONProtoToDSL(&modelPb, language.WithIncludeSourceInformation(true)) if err != nil { return nil, fmt.Errorf("error transforming from JSON due to: %w", err) } diff --git a/internal/authorizationmodel/read-from-input.go b/internal/authorizationmodel/read-from-input.go index 7344a54..3f6be7e 100644 --- a/internal/authorizationmodel/read-from-input.go +++ b/internal/authorizationmodel/read-from-input.go @@ -43,11 +43,19 @@ func ReadFromFile( // if the input format is set as the default, set it from the file extension (and default to fga) if *format == ModelFormatDefault { - if strings.HasSuffix(fileName, "json") { + switch { + case strings.HasSuffix(fileName, "fga.mod"): + *format = ModelFormatModular + *input = fileName + case strings.HasSuffix(fileName, "json"): *format = ModelFormatJSON - } else { + default: *format = ModelFormatFGA } + } else if *format == ModelFormatModular { + // If we're provided a modular model we want the input to be the filename as we will handle + // reading and parsing the fga.mod file later + *input = fileName } if *storeName == "" { diff --git a/internal/storetest/localstore.go b/internal/storetest/localstore.go index 9221399..a39e676 100644 --- a/internal/storetest/localstore.go +++ b/internal/storetest/localstore.go @@ -87,20 +87,19 @@ func getLocalServerModelAndTuples( // If we have at least one local test, initialize the local server datastore := memory.New() - fgaServer, err := server.NewServerWithOpts(server.WithDatastore(datastore)) + fgaServer, err := server.NewServerWithOpts( + server.WithDatastore(datastore), + server.WithExperimentals(server.ExperimentalEnableModularModels), + ) if err != nil { return nil, nil, stopServerFn, err //nolint:wrapcheck } tempModel := authorizationmodel.AuthzModel{} - if format == authorizationmodel.ModelFormatJSON { - if err := tempModel.ReadFromJSONString(storeData.Model); err != nil { - return nil, nil, stopServerFn, err //nolint:wrapcheck - } - } else { - if err := tempModel.ReadFromDSLString(storeData.Model); err != nil { - return nil, nil, stopServerFn, err //nolint:wrapcheck - } + + err = tempModel.ReadModelFromString(storeData.Model, format) + if err != nil { + return nil, nil, stopServerFn, err //nolint:wrapcheck } authModel = &tempModel