@@ -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
2223var 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+
609775func warrantCheckAsString (w fga.WarrantCheck ) (string , error ) {
610776 s := fmt .Sprintf (
611777 "%s:%s %s %s:%s" ,
0 commit comments