Skip to content
This repository was archived by the owner on Feb 25, 2026. It is now read-only.

Commit d46f62c

Browse files
authored
Merge pull request #13 from workos/at-convert-schemas
[FGA-98] Add `schema convert` and `schema apply` to FGA module
2 parents 5d61376 + e1266f3 commit d46f62c

3 files changed

Lines changed: 169 additions & 3 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ require (
88
github.com/pkg/errors v0.9.1
99
github.com/spf13/cobra v1.8.1
1010
github.com/spf13/viper v1.19.0
11-
github.com/workos/workos-go/v4 v4.16.0
11+
github.com/workos/workos-go/v4 v4.21.0
1212
)
1313

1414
require (

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
113113
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
114114
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
115115
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
116-
github.com/workos/workos-go/v4 v4.16.0 h1:7BPvVKHI8qhOY/lH4EbBY2CtQs1CDJxXDbfTnYNma3g=
117-
github.com/workos/workos-go/v4 v4.16.0/go.mod h1:CwpXdAWhIE3SxV49qBVeYqWV8ojv0A0L9nM1xnho4/c=
116+
github.com/workos/workos-go/v4 v4.21.0 h1:pEoAJzCsBPU46dL6/PwwwS5BrBV8LWOZQp0mERrRPCc=
117+
github.com/workos/workos-go/v4 v4.21.0/go.mod h1:CwpXdAWhIE3SxV49qBVeYqWV8ojv0A0L9nM1xnho4/c=
118118
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
119119
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
120120
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=

internal/cmd/fga.go

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/spf13/cobra"
1818
"github.com/workos/workos-cli/internal/printer"
1919
"github.com/workos/workos-go/v4/pkg/fga"
20+
"github.com/workos/workos-go/v4/pkg/workos_errors"
2021
)
2122

2223
var resourceTypesFile string
@@ -65,6 +66,15 @@ func init() {
6566
queryCmd.Flags().String("order", "", "order in which a list of results should be returned (asc or desc)")
6667
fgaCmd.AddCommand(queryCmd)
6768

69+
// schema
70+
convertSchemaCMD.Flags().String("to", "json", "output to (schema or json)")
71+
convertSchemaCMD.Flags().String("output", "pretty", "output pretty or raw. use raw for machine-readable output or writing to a file")
72+
schemaCmd.AddCommand(convertSchemaCMD)
73+
applySchemaCmd.Flags().BoolP("verbose", "v", false, "print extra details about the request")
74+
applySchemaCmd.Flags().Bool("strict", false, "fail if there are warnings")
75+
schemaCmd.AddCommand(applySchemaCmd)
76+
fgaCmd.AddCommand(schemaCmd)
77+
6878
rootCmd.AddCommand(fgaCmd)
6979
}
7080

@@ -606,6 +616,162 @@ var queryCmd = &cobra.Command{
606616
},
607617
}
608618

619+
var schemaCmd = &cobra.Command{
620+
Use: "schema",
621+
Short: "Manage your schema",
622+
Long: "A schema is a set of resource types and relations that define the structure of your application. Use this command to manage your schema.",
623+
}
624+
625+
var convertSchemaCMD = &cobra.Command{
626+
Use: "convert <input_file>",
627+
Short: "Convert a schema to a JSON representation (or JSON to schema)",
628+
Long: "Convert a schema to a JSON representation (or JSON to schema) that can be used to apply resource types.",
629+
Example: `workos fga schema convert schema.txt -o json`,
630+
Args: cobra.ExactArgs(1),
631+
RunE: func(cmd *cobra.Command, args []string) error {
632+
to, err := cmd.Flags().GetString("to")
633+
if err != nil {
634+
return errors.Wrap(err, "invalid to flag")
635+
}
636+
output, err := cmd.Flags().GetString("output")
637+
if err != nil {
638+
return errors.Wrap(err, "invalid output flag")
639+
}
640+
641+
bytes, err := os.ReadFile(args[0])
642+
if err != nil {
643+
return errors.Errorf("error reading input file: %v", err)
644+
}
645+
646+
var response fga.ConvertSchemaResponse
647+
switch to {
648+
case "json":
649+
schemaString := string(bytes)
650+
response, err = fga.ConvertSchemaToResourceTypes(context.Background(), fga.ConvertSchemaToResourceTypesOpts{
651+
Schema: schemaString,
652+
})
653+
if err != nil {
654+
return convertSchemaError(err)
655+
}
656+
case "schema":
657+
var resourceTypesWithVersion fga.ConvertResourceTypesToSchemaOpts
658+
err = json.Unmarshal(bytes, &resourceTypesWithVersion)
659+
if err != nil {
660+
return errors.Errorf("error unmarshalling resource types: %v", err)
661+
}
662+
response, err = fga.ConvertResourceTypesToSchema(context.Background(), resourceTypesWithVersion)
663+
if err != nil {
664+
return convertSchemaError(err)
665+
}
666+
default:
667+
return errors.Errorf("invalid conversion: %s", to)
668+
}
669+
670+
switch output {
671+
case "pretty":
672+
printer.PrintMsg("Version:")
673+
printer.PrintMsg(fmt.Sprintf("%s\n", response.Version))
674+
675+
if response.Warnings != nil {
676+
printer.PrintMsg("Warnings:")
677+
for _, warning := range response.Warnings {
678+
printer.PrintMsg(warning.Message)
679+
}
680+
printer.PrintMsg("\n")
681+
}
682+
683+
if response.Schema != nil {
684+
printer.PrintMsg("Schema:")
685+
printer.PrintMsg(*response.Schema)
686+
}
687+
688+
if response.ResourceTypes != nil {
689+
printer.PrintMsg("Resource Types:")
690+
printer.PrintJson(response.ResourceTypes)
691+
}
692+
case "raw":
693+
if response.Schema != nil {
694+
printer.PrintMsg(*response.Schema)
695+
} else {
696+
printer.PrintJson(response.ResourceTypes)
697+
}
698+
default:
699+
return errors.Errorf("invalid output: %s", output)
700+
}
701+
return nil
702+
},
703+
}
704+
705+
var applySchemaCmd = &cobra.Command{
706+
Use: "apply <input_file>",
707+
Short: "Apply a schema",
708+
Long: "Apply a schema to create or update resource types.",
709+
Example: `workos fga schema apply schema.txt`,
710+
Args: cobra.ExactArgs(1),
711+
RunE: func(cmd *cobra.Command, args []string) error {
712+
verbose, err := cmd.Flags().GetBool("verbose")
713+
if err != nil {
714+
return errors.Wrap(err, "invalid verbose flag")
715+
}
716+
strict, err := cmd.Flags().GetBool("strict")
717+
if err != nil {
718+
return errors.Wrap(err, "invalid strict flag")
719+
}
720+
721+
bytes, err := os.ReadFile(args[0])
722+
if err != nil {
723+
return errors.Errorf("error reading input file: %v", err)
724+
}
725+
// Convert schema to resource types
726+
response, err := fga.ConvertSchemaToResourceTypes(context.Background(), fga.ConvertSchemaToResourceTypesOpts{
727+
Schema: string(bytes),
728+
})
729+
if err != nil {
730+
return convertSchemaError(err)
731+
}
732+
733+
if response.Warnings != nil {
734+
printer.PrintMsg("Warnings:")
735+
for _, warning := range response.Warnings {
736+
printer.PrintMsg(warning.Message)
737+
}
738+
printer.PrintMsg("\n")
739+
if strict {
740+
return errors.New("error applying schema: warnings found (omit --strict to ignore)")
741+
}
742+
}
743+
744+
printer.PrintMsg("applying schema...")
745+
746+
if verbose {
747+
printer.PrintJson(response.ResourceTypes)
748+
}
749+
750+
ops := make([]fga.UpdateResourceTypeOpts, 0)
751+
for _, rt := range response.ResourceTypes {
752+
ops = append(ops, fga.UpdateResourceTypeOpts(rt))
753+
}
754+
755+
_, err = fga.BatchUpdateResourceTypes(context.Background(), ops)
756+
if err != nil {
757+
return errors.Errorf("error applying schema: %v", err)
758+
}
759+
760+
printer.PrintMsg("Schema applied")
761+
return nil
762+
},
763+
}
764+
765+
func convertSchemaError(err error) error {
766+
var target workos_errors.HTTPError
767+
if errors.As(err, &target) {
768+
if len(target.Errors) > 0 {
769+
return errors.Errorf("error converting schema: %s\n\t%s", target.Message, strings.Join(target.Errors, "\n\t"))
770+
}
771+
}
772+
return errors.Errorf("error converting schema: %v", err)
773+
}
774+
609775
func warrantCheckAsString(w fga.WarrantCheck) (string, error) {
610776
s := fmt.Sprintf(
611777
"%s:%s %s %s:%s",

0 commit comments

Comments
 (0)