2020-02-24 19:14:46 +03:00
package commands
import (
"fmt"
"strings"
2021-06-16 14:44:15 +03:00
"github.com/hasura/graphql-engine/cli/v2/internal/hasura"
2021-03-08 14:59:35 +03:00
2021-06-16 14:44:15 +03:00
"github.com/hasura/graphql-engine/cli/v2"
2022-11-07 16:51:15 +03:00
"github.com/hasura/graphql-engine/cli/v2/internal/errors"
2021-06-16 14:44:15 +03:00
"github.com/hasura/graphql-engine/cli/v2/internal/metadataobject/actions"
"github.com/hasura/graphql-engine/cli/v2/internal/metadataobject/actions/types"
"github.com/hasura/graphql-engine/cli/v2/util"
2022-11-07 16:51:15 +03:00
2020-02-24 19:14:46 +03:00
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func newActionsCreateCmd ( ec * cli . ExecutionContext , v * viper . Viper ) * cobra . Command {
opts := & actionsCreateOptions {
EC : ec ,
}
actionsCreateCmd := & cobra . Command {
Use : "create [action-name]" ,
2022-12-30 06:50:48 +03:00
Short : "Create a Hasura Action" ,
Long : "This command allows you to create an Action to extend Hasura's schema with custom business logic using queries and mutations. Optional flags can be used to derive the Action from an existing GraphQL query or mutation. Additionally, codegen can be bundled with the creation of the Action to provide you ready-to-use boilerplate with your framework of choice." ,
Example : ` # Create a Hasura Action
2020-02-24 19:14:46 +03:00
hasura actions create [ action - name ]
2022-12-30 06:50:48 +03:00
# Create a Hasura Action with codegen
2021-03-15 18:40:52 +03:00
hasura actions create [ action - name ] -- with - codegen
2020-02-24 19:14:46 +03:00
2022-12-30 06:50:48 +03:00
# Create a Hasura Action by deriving from a hasura operation
2020-02-24 19:14:46 +03:00
hasura actions create [ action - name ] -- derive - from ' '
2022-12-30 06:50:48 +03:00
# Create a Hasura Action with a different kind or webhook
2020-02-24 19:14:46 +03:00
hasura actions create [ action - name ] -- kind [ synchronous | asynchronous ] -- webhook [ http : //localhost:3000]`,
SilenceUsage : true ,
Args : cobra . ExactArgs ( 1 ) ,
RunE : func ( cmd * cobra . Command , args [ ] string ) error {
2022-11-07 16:51:15 +03:00
op := genOpName ( cmd , "RunE" )
2020-02-24 19:14:46 +03:00
opts . name = args [ 0 ]
2022-11-07 16:51:15 +03:00
if err := opts . run ( ) ; err != nil {
return errors . E ( op , err )
}
return nil
2020-02-24 19:14:46 +03:00
} ,
}
f := actionsCreateCmd . Flags ( )
f . StringVar ( & opts . deriveFrom , "derive-from" , "" , "derive action from a Hasura operation" )
f . BoolVar ( & opts . withCodegen , "with-codegen" , false , "create action along with codegen" )
f . String ( "kind" , "" , "kind to use in action" )
f . String ( "webhook" , "" , "webhook to use in action" )
// bind to viper
2020-04-08 14:55:42 +03:00
util . BindPFlag ( v , "actions.kind" , f . Lookup ( "kind" ) )
util . BindPFlag ( v , "actions.handler_webhook_baseurl" , f . Lookup ( "webhook" ) )
2020-02-24 19:14:46 +03:00
return actionsCreateCmd
}
type actionsCreateOptions struct {
EC * cli . ExecutionContext
name string
deriveFrom string
withCodegen bool
}
func ( o * actionsCreateOptions ) run ( ) error {
2022-11-07 16:51:15 +03:00
var op errors . Op = "commands.actionsCreateOptions.run"
2021-04-12 20:27:33 +03:00
var introSchema hasura . IntrospectionSchema
var err error
2020-02-24 19:14:46 +03:00
if o . deriveFrom != "" {
o . deriveFrom = strings . TrimSpace ( o . deriveFrom )
o . EC . Spin ( "Deriving a Hasura operation..." )
2021-04-12 20:27:33 +03:00
introSchema , err = o . EC . APIClient . V1Graphql . GetIntrospectionSchema ( )
2020-02-24 19:14:46 +03:00
if err != nil {
2022-11-07 16:51:15 +03:00
return errors . E ( op , fmt . Errorf ( "error in fetching introspection schema: %w" , err ) )
2020-02-24 19:14:46 +03:00
}
o . EC . Spinner . Stop ( )
}
// create new action
o . EC . Spin ( "Creating the action..." )
actionCfg := actions . New ( o . EC , o . EC . MetadataDir )
2020-04-22 12:15:42 +03:00
o . EC . Spinner . Stop ( )
2020-02-24 19:14:46 +03:00
err = actionCfg . Create ( o . name , introSchema , o . deriveFrom )
if err != nil {
2022-11-07 16:51:15 +03:00
return errors . E ( op , fmt . Errorf ( "error in creating action: %w" , err ) )
2020-02-24 19:14:46 +03:00
}
2021-05-14 22:09:01 +03:00
opts := & MetadataApplyOptions {
EC : o . EC ,
}
err = opts . Run ( )
2020-02-24 19:14:46 +03:00
if err != nil {
2022-11-07 16:51:15 +03:00
return errors . E ( op , fmt . Errorf ( "error in applying metadata: %w" , err ) )
2020-02-24 19:14:46 +03:00
}
o . EC . Logger . WithField ( "name" , o . name ) . Infoln ( "action created" )
// if codegen config not present, skip codegen
if o . EC . Config . ActionConfig . Codegen . Framework == "" {
if o . withCodegen {
2022-11-07 16:51:15 +03:00
return errors . E ( op , fmt . Errorf ( ` could not find codegen config . For adding codegen config , run :
2020-02-24 19:14:46 +03:00
2022-11-07 16:51:15 +03:00
hasura actions use - codegen ` ) )
2020-02-24 19:14:46 +03:00
}
return nil
}
// if with-codegen flag not present, ask them if they want to codegen
2021-09-07 16:33:58 +03:00
var confirmation bool
2020-02-24 19:14:46 +03:00
if ! o . withCodegen {
confirmation , err = util . GetYesNoPrompt ( "Do you want to generate " + o . EC . Config . ActionConfig . Codegen . Framework + " code for this action and the custom types?" )
if err != nil {
2022-11-07 16:51:15 +03:00
return errors . E ( op , fmt . Errorf ( "error in getting user input: %w" , err ) )
2020-02-24 19:14:46 +03:00
}
}
2021-09-07 16:33:58 +03:00
if ! confirmation {
2020-02-24 19:14:46 +03:00
infoMsg := fmt . Sprintf ( ` You skipped codegen . For getting codegen for this action , run :
hasura actions codegen % s
` , o . name )
o . EC . Logger . Info ( infoMsg )
return nil
}
2021-11-10 12:21:06 +03:00
if err := o . EC . SetupCodegenAssetsRepo ( ) ; err != nil {
o . EC . Logger . Errorf ( "failed generating code: setting up codegen-assets repo failed (this is required for automatically generating actions code): %v" , err )
o . EC . Logger . Errorf ( "retry operation with: 'hasura actions codegen %s'" , o . name )
return nil
}
// ensure codegen-assets repo exists
if err := ec . CodegenAssetsRepo . EnsureCloned ( ) ; err != nil {
o . EC . Logger . Errorf ( "failed generating code: pulling latest actions codegen files from internet failed: %v" , err )
o . EC . Logger . Errorf ( "retry operation with: 'hasura actions codegen %s'" , o . name )
return nil
}
2020-02-24 19:14:46 +03:00
// construct derive payload to send to codegenerator
derivePayload := types . DerivePayload {
IntrospectionSchema : introSchema ,
Operation : o . deriveFrom ,
ActionName : o . name ,
}
// Run codegen
o . EC . Spin ( fmt . Sprintf ( ` Running "hasura actions codegen %s"... ` , o . name ) )
err = actionCfg . Codegen ( o . name , derivePayload )
if err != nil {
2021-11-10 12:21:06 +03:00
o . EC . Spinner . Stop ( )
o . EC . Logger . Warn ( "codegen failed, retry with `hasura actions codegen`" )
2022-11-07 16:51:15 +03:00
return errors . E ( op , err )
2020-02-24 19:14:46 +03:00
}
o . EC . Spinner . Stop ( )
o . EC . Logger . Info ( "Codegen files generated at " + o . EC . Config . ActionConfig . Codegen . OutputDir )
return nil
}