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" ,
2023-01-09 07:25:53 +03:00
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 .
Further Reading :
- https : //hasura.io/docs/latest/actions/create/
` ,
2022-12-30 06:50:48 +03:00
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
2023-01-11 11:37:47 +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" )
2023-01-11 11:37:47 +03:00
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" )
2020-02-24 19:14:46 +03:00
// 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
}