From 96f6bdd531bb86118276929b031d9c64a62b0a43 Mon Sep 17 00:00:00 2001 From: Aravind Date: Mon, 27 Jul 2020 07:15:43 +0530 Subject: [PATCH] cli: support squashing actions in config v1 (#5249) --- cli/migrate/database/hasuradb/squash.go | 298 ++++++++++++++++++++++++ cli/migrate/database/hasuradb/types.go | 113 ++++++++- 2 files changed, 403 insertions(+), 8 deletions(-) diff --git a/cli/migrate/database/hasuradb/squash.go b/cli/migrate/database/hasuradb/squash.go index b39e9eb0cd3..3be39327393 100644 --- a/cli/migrate/database/hasuradb/squash.go +++ b/cli/migrate/database/hasuradb/squash.go @@ -17,6 +17,212 @@ import ( type CustomQuery linq.Query +func (q CustomQuery) MergeCustomTypes(squashList *database.CustomList) error { + actionPermissionsTransition := transition.New(&cronTriggerConfig{}) + actionPermissionsTransition.Initial("new") + actionPermissionsTransition.State("created") + + actionPermissionsTransition.Event(setCustomTypes).To("created").From("new", "created") + + next := q.Iterate() + + for item, ok := next(); ok; item, ok = next() { + g := item.(linq.Group) + if g.Key == "" { + continue + } + var first *list.Element + for ind, val := range g.Group { + element := val.(*list.Element) + switch obj := element.Value.(type) { + case *setCustomTypesInput: + if ind == 0 { + first = element + continue + } + first.Value = obj + squashList.Remove(element) + } + } + } + return nil +} + +func (q CustomQuery) MergeActionPermissions(squashList *database.CustomList) error { + actionPermissionsTransition := transition.New(&actionPermissionConfig{}) + actionPermissionsTransition.Initial("new") + actionPermissionsTransition.State("created") + actionPermissionsTransition.State("deleted") + + actionPermissionsTransition.Event(createActionPermission).To("created").From("new", "deleted") + actionPermissionsTransition.Event(dropActionPermission).To("deleted").From("new", "created") + + next := q.Iterate() + + for item, ok := next(); ok; item, ok = next() { + g := item.(linq.Group) + if g.Key == "" { + continue + } + key := g.Key.(string) + cfg := actionPermissionConfig{ + action: key, + } + prevElems := make([]*list.Element, 0) + for _, val := range g.Group { + element := val.(*list.Element) + switch element.Value.(type) { + case *createActionPermissionInput: + err := actionPermissionsTransition.Trigger(createActionPermission, &cfg, nil) + if err != nil { + return err + } + prevElems = append(prevElems, element) + case *dropActionPermissionInput: + if cfg.GetState() == "created" { + prevElems = append(prevElems, element) + } + err := actionPermissionsTransition.Trigger(dropActionPermission, &cfg, nil) + if err != nil { + return err + } + for _, e := range prevElems { + squashList.Remove(e) + } + } + } + } + return nil +} + +func (q CustomQuery) MergeActions(squashList *database.CustomList) error { + actionTransition := transition.New(&actionConfig{}) + actionTransition.Initial("new") + actionTransition.State("created") + actionTransition.State("updated") + actionTransition.State("deleted") + + actionTransition.Event(createAction).To("created").From("new", "deleted") + actionTransition.Event(updateAction).To("updated").From("new", "created", "updated", "deleted") + actionTransition.Event(dropAction).To("deleted").From("new", "created", "updated") + + next := q.Iterate() + + for item, ok := next(); ok; item, ok = next() { + g := item.(linq.Group) + if g.Key == "" { + continue + } + key, ok := g.Key.(string) + if !ok { + continue + } + cfg := actionConfig{ + name: key, + } + prevElems := make([]*list.Element, 0) + for _, val := range g.Group { + element := val.(*list.Element) + switch obj := element.Value.(type) { + case *createActionInput: + err := actionTransition.Trigger(createAction, &cfg, nil) + if err != nil { + return errors.Wrapf(err, "error squashin Action: %v", obj.Name) + } + prevElems = append(prevElems, element) + case *updateActionInput: + if len(prevElems) != 0 { + if _, ok := prevElems[0].Value.(*createActionInput); ok { + prevElems[0].Value = &createActionInput{ + actionDefinition: obj.actionDefinition, + } + prevElems = prevElems[:1] + + err := actionTransition.Trigger(dropAction, &cfg, nil) + if err != nil { + return errors.Wrapf(err, "error squashing action: %v", obj.Name) + } + squashList.Remove(element) + + err = actionTransition.Trigger(createAction, &cfg, nil) + if err != nil { + return errors.Wrapf(err, "error squashing action: %v", obj.Name) + } + continue + } + + for _, e := range prevElems { + squashList.Remove(e) + } + prevElems = prevElems[:0] + err := actionTransition.Trigger(dropAction, &cfg, nil) + if err != nil { + return errors.Wrapf(err, "error squashing action: %v", obj.Name) + } + + } + + prevElems = append(prevElems, element) + err := actionTransition.Trigger(updateAction, &cfg, nil) + if err != nil { + return errors.Wrapf(err, "error squashing: %v", obj.Name) + } + case *dropActionInput: + if cfg.GetState() == "created" { + prevElems = append(prevElems, element) + // drop action permissions as well + actionPermissionGroup := CustomQuery(linq.FromIterable(squashList).GroupByT( + func(element *list.Element) string { + switch args := element.Value.(type) { + case *createActionPermissionInput: + if v, ok := args.Action.(string); ok { + return v + } + case *dropActionPermissionInput: + if v, ok := args.Action.(string); ok { + return v + } + } + return "" + }, func(element *list.Element) *list.Element { + return element + }, + )) + + next := actionPermissionGroup.Iterate() + + for item, ok := next(); ok; item, ok = next() { + g := item.(linq.Group) + if g.Key == "" { + continue + } + key, ok := g.Key.(string) + if !ok { + continue + } + if key == obj.Name { + for _, val := range g.Group { + element := val.(*list.Element) + squashList.Remove(element) + } + } + } + } + + err := actionTransition.Trigger(dropAction, &cfg, nil) + if err != nil { + return err + } + for _, e := range prevElems { + squashList.Remove(e) + } + prevElems = prevElems[:0] + } + } + } + return nil +} + func (q CustomQuery) MergeCronTriggers(squashList *database.CustomList) error { cronTriggersTransition := transition.New(&cronTriggerConfig{}) cronTriggersTransition.Initial("new") @@ -975,6 +1181,21 @@ func (h *HasuraDB) PushToList(migration io.Reader, fileType string, l *database. } l.PushBack(o) } + case *createActionInput, *updateActionInput: + if v.Type == updateAction { + o, ok := v.Args.(*updateActionInput) + if !ok { + break + } + l.PushBack(o) + } + if v.Type == createAction { + o, ok := v.Args.(*createActionInput) + if !ok { + break + } + l.PushBack(o) + } default: l.PushBack(actionType) } @@ -1437,6 +1658,70 @@ func (h *HasuraDB) Squash(l *database.CustomList, ret chan<- interface{}) { ret <- err } + customTypesGroup := CustomQuery(linq.FromIterable(l).GroupByT( + func(element *list.Element) string { + switch element.Value.(type) { + case *setCustomTypesInput: + return setCustomTypes + } + return "" + }, func(element *list.Element) *list.Element { + return element + }, + )) + err = customTypesGroup.MergeCustomTypes(l) + if err != nil { + ret <- err + } + + actionGroup := CustomQuery(linq.FromIterable(l).GroupByT( + func(element *list.Element) string { + switch args := element.Value.(type) { + case *createActionInput: + if v, ok := args.Name.(string); ok { + return v + } + case *updateActionInput: + if v, ok := args.Name.(string); ok { + return v + } + case *dropActionInput: + if v, ok := args.Name.(string); ok { + return v + } + } + return "" + }, func(element *list.Element) *list.Element { + return element + }, + )) + err = actionGroup.MergeActions(l) + if err != nil { + ret <- err + } + + actionPermissionGroup := CustomQuery(linq.FromIterable(l).GroupByT( + func(element *list.Element) string { + switch args := element.Value.(type) { + case *createActionPermissionInput: + if v, ok := args.Action.(string); ok { + return v + } + case *dropActionPermissionInput: + if v, ok := args.Action.(string); ok { + return v + } + } + return "" + }, func(element *list.Element) *list.Element { + return element + }, + )) + err = actionPermissionGroup.MergeActionPermissions(l) + if err != nil { + ret <- err + } + for e := l.Front(); e != nil; e = e.Next() { q := HasuraInterfaceQuery{ Args: e.Value, @@ -1520,10 +1805,23 @@ func (h *HasuraDB) Squash(l *database.CustomList, ret chan<- interface{}) { q.Type = createCronTrigger case *deleteCronTriggerInput: q.Type = deleteCronTrigger + case *createActionInput: + q.Type = createAction + case *updateActionInput: + q.Type = updateAction + case *dropActionInput: + q.Type = dropAction + case *createActionPermissionInput: + q.Type = createActionPermission + case *dropActionPermissionInput: + q.Type = dropActionPermission + case *setCustomTypesInput: + q.Type = setCustomTypes case *RunSQLInput: ret <- []byte(args.SQL) continue default: + h.logger.Debug("cannot find metadata type for:", args) ret <- fmt.Errorf("invalid metadata action") return } diff --git a/cli/migrate/database/hasuradb/types.go b/cli/migrate/database/hasuradb/types.go index 51bcf25ba0f..acbcc052152 100644 --- a/cli/migrate/database/hasuradb/types.go +++ b/cli/migrate/database/hasuradb/types.go @@ -80,6 +80,51 @@ type deleteCronTriggerInput struct { Name string `json:"name" yaml:"name"` } +type actionDefinition struct { + Name interface{} `json:"name,omitempty" yaml:"name,omitempty"` + Definition interface{} `json:"definition,omitempty" yaml:"definition,omitempty"` +} + +type createActionInput struct { + actionDefinition + Comment string `json:"comment,omitempty" yaml:"comment,omitempty"` +} + +type actionAndPermission struct { + actionDefinition + Permissions []PermissionDefinition `json:"permissions" yaml:"permissions"` +} + +type dropActionInput struct { + Name interface{} `json:"name,omitempty" yaml:"name,omitempty"` + ClearData bool `json:"clear_data,omitempty" yaml:"clear_data,omitempty"` +} + +type updateActionInput struct { + actionDefinition +} + +type PermissionDefinition struct { + Role interface{} `json:"role,omitempty" yaml:"role,omitempty"` + Comment string `json:"comment,omitempty" yaml:"comment,omitempty"` +} +type createActionPermissionInput struct { + Action interface{} `json:"action,omitempty" yaml:"action,omitempty"` + PermissionDefinition +} + +type dropActionPermissionInput struct { + Action interface{} `json:"action,omitempty" yaml:"action,omitempty"` + PermissionDefinition +} + +type setCustomTypesInput struct { + InputObjects interface{} `json:"input_objects,omitempty" yaml:"input_objects,omitempty"` + Objects interface{} `json:"objects,omitempty" yaml:"objects,omitempty"` + Scalars interface{} `json:"scalars,omitempty" yaml:"scalars,omitempty"` + Enums interface{} `json:"enums,omitempty" yaml:"enums,omitempty"` +} + func (h *newHasuraIntefaceQuery) UnmarshalJSON(b []byte) error { type t newHasuraIntefaceQuery var q t @@ -175,6 +220,18 @@ func (h *newHasuraIntefaceQuery) UnmarshalJSON(b []byte) error { q.Args = &createCronTriggerInput{} case deleteCronTrigger: q.Args = &deleteCronTriggerInput{} + case createAction: + q.Args = &createActionInput{} + case dropAction: + q.Args = &dropActionInput{} + case updateAction: + q.Args = &updateActionInput{} + case createActionPermission: + q.Args = &createActionPermissionInput{} + case dropActionPermission: + q.Args = &dropActionPermissionInput{} + case setCustomTypes: + q.Args = &setCustomTypesInput{} default: return fmt.Errorf("cannot squash type %s", q.Type) } @@ -357,6 +414,12 @@ const ( deleteRemoteRelationship = "delete_remote_relationship" createCronTrigger = "create_cron_trigger" deleteCronTrigger = "delete_cron_trigger" + createAction = "create_action" + dropAction = "drop_action" + updateAction = "update_action" + createActionPermission = "create_action_permission" + dropActionPermission = "drop_action_permission" + setCustomTypes = "set_custom_types" ) type tableMap struct { @@ -700,6 +763,8 @@ type replaceMetadataInput struct { AllowList []*addCollectionToAllowListInput `json:"allowlist" yaml:"allowlist"` RemoteSchemas []*addRemoteSchemaInput `json:"remote_schemas" yaml:"remote_schemas"` CronTriggers []*createCronTriggerInput `json:"cron_triggers" yaml:"cron_triggers"` + Actions []*actionAndPermission `json:"actions" yaml:"actions"` + CustomTypes *setCustomTypesInput `json:"custom_types" yaml:"custom_types"` } func (rmi *replaceMetadataInput) convertToMetadataActions(l *database.CustomList) { @@ -806,15 +871,17 @@ func (rmi *replaceMetadataInput) convertToMetadataActions(l *database.CustomList } for _, table := range rmi.Tables { - for _, remoteRelationship := range *table.RemoteRelationships { - r := createRemoteRelationshipInput{ - remoteRelationshipDefinition: remoteRelationship.Definiton, - Table: tableSchema{ - Name: table.Table.Name, - Schema: table.Table.Schema, - }, + if table.RemoteRelationships != nil { + for _, remoteRelationship := range *table.RemoteRelationships { + r := createRemoteRelationshipInput{ + remoteRelationshipDefinition: remoteRelationship.Definiton, + Table: tableSchema{ + Name: table.Table.Name, + Schema: table.Table.Schema, + }, + } + l.PushBack(r) } - l.PushBack(r) } } @@ -842,6 +909,26 @@ func (rmi *replaceMetadataInput) convertToMetadataActions(l *database.CustomList for _, ct := range rmi.CronTriggers { l.PushBack(ct) } + + // track actions + for _, action := range rmi.Actions { + // action definition + a := &createActionInput{ + actionDefinition: action.actionDefinition, + } + l.PushBack(a) + // permission + for _, permission := range action.Permissions { + p := &createActionPermissionInput{ + Action: action.Name, + PermissionDefinition: permission, + } + l.PushBack(p) + } + } + if rmi.CustomTypes != nil { + l.PushBack(rmi.CustomTypes) + } } type InconsistentMetadata struct { @@ -1022,7 +1109,17 @@ type remoteRelationshipConfig struct { tableName, schemaName, name string transition.Transition } + type cronTriggerConfig struct { name string transition.Transition } + +type actionConfig struct { + name string + transition.Transition +} +type actionPermissionConfig struct { + action string + transition.Transition +}