SEBA-671 Add model create and model sync commands
Change-Id: Ic5f3f0eb8c5c4eb635fb1bdcb818fbafde2b9001
diff --git a/commands/models.go b/commands/models.go
index bae5dbc..33c3793 100644
--- a/commands/models.go
+++ b/commands/models.go
@@ -24,8 +24,10 @@
)
const (
+ DEFAULT_CREATE_FORMAT = "table{{ .Id }}\t{{ .Message }}"
DEFAULT_DELETE_FORMAT = "table{{ .Id }}\t{{ .Message }}"
DEFAULT_UPDATE_FORMAT = "table{{ .Id }}\t{{ .Message }}"
+ DEFAULT_SYNC_FORMAT = "table{{ .Id }}\t{{ .Message }}"
)
type ModelNameString string
@@ -43,10 +45,12 @@
type ModelUpdate struct {
OutputOptions
- Filter string `short:"f" long:"filter" description:"Comma-separated list of filters"`
- SetFields string `long:"set-field" description:"Comma-separated list of field=value to set"`
- SetJSON string `long:"set-json" description:"JSON dictionary to use for settings fields"`
- Args struct {
+ Unbuffered bool `short:"u" long:"unbuffered" description:"Do not buffer console output and suppress default output processor"`
+ Filter string `short:"f" long:"filter" description:"Comma-separated list of filters"`
+ SetFields string `long:"set-field" description:"Comma-separated list of field=value to set"`
+ SetJSON string `long:"set-json" description:"JSON dictionary to use for settings fields"`
+ Sync bool `long:"sync" description:"Synchronize before returning"`
+ Args struct {
ModelName ModelNameString
} `positional-args:"yes" required:"yes"`
IDArgs struct {
@@ -56,8 +60,32 @@
type ModelDelete struct {
OutputOptions
- Filter string `short:"f" long:"filter" description:"Comma-separated list of filters"`
- Args struct {
+ Unbuffered bool `short:"u" long:"unbuffered" description:"Do not buffer console output and suppress default output processor"`
+ Filter string `short:"f" long:"filter" description:"Comma-separated list of filters"`
+ Args struct {
+ ModelName ModelNameString
+ } `positional-args:"yes" required:"yes"`
+ IDArgs struct {
+ ID []int32
+ } `positional-args:"yes" required:"no"`
+}
+
+type ModelCreate struct {
+ OutputOptions
+ Unbuffered bool `short:"u" long:"unbuffered" description:"Do not buffer console output"`
+ SetFields string `long:"set-field" description:"Comma-separated list of field=value to set"`
+ SetJSON string `long:"set-json" description:"JSON dictionary to use for settings fields"`
+ Sync bool `long:"sync" description:"Synchronize before returning"`
+ Args struct {
+ ModelName ModelNameString
+ } `positional-args:"yes" required:"yes"`
+}
+
+type ModelSync struct {
+ OutputOptions
+ Unbuffered bool `short:"u" long:"unbuffered" description:"Do not buffer console output and suppress default output processor"`
+ Filter string `short:"f" long:"filter" description:"Comma-separated list of filters"`
+ Args struct {
ModelName ModelNameString
} `positional-args:"yes" required:"yes"`
IDArgs struct {
@@ -69,16 +97,18 @@
List ModelList `command:"list"`
Update ModelUpdate `command:"update"`
Delete ModelDelete `command:"delete"`
+ Create ModelCreate `command:"create"`
+ Sync ModelSync `command:"sync"`
}
type ModelStatusOutputRow struct {
- Id int32 `json:"id"`
- Message string `json:"message"`
+ Id interface{} `json:"id"`
+ Message string `json:"message"`
}
type ModelStatusOutput struct {
- Rows []ModelStatusOutputRow
- Quiet bool
+ Rows []ModelStatusOutputRow
+ Unbuffered bool
}
var modelOpts = ModelOpts{}
@@ -87,27 +117,33 @@
parser.AddCommand("model", "model commands", "Commands to query and manipulate XOS models", &modelOpts)
}
-func InitModelStatusOutput(quiet bool, count int) ModelStatusOutput {
- if quiet {
- return ModelStatusOutput{Quiet: quiet}
- } else {
- return ModelStatusOutput{Rows: make([]ModelStatusOutputRow, count), Quiet: quiet}
- }
+// Initialize ModelStatusOutput structure, creating a row for each model that will be output
+func InitModelStatusOutput(unbuffered bool, count int) ModelStatusOutput {
+ return ModelStatusOutput{Rows: make([]ModelStatusOutputRow, count), Unbuffered: unbuffered}
}
-func UpdateModelStatusOutput(output *ModelStatusOutput, i int, id int32, status string, err error) {
+// Update model status output row for the model
+// If unbuffered is set then we will output directly to the console. Regardless of the unbuffered
+// setting, we always update the row, as callers may check that row for status.
+// Args:
+// output - ModelStatusOutput struct to update
+// i - index of row to update
+// id - id of model, <nil> if no model exists
+// status - status text to set if there is no error
+// errror - if non-nil, then apply error text instead of status text
+// final - true if successful status should be reported, false if successful status is yet to come
+
+func UpdateModelStatusOutput(output *ModelStatusOutput, i int, id interface{}, status string, err error, final bool) {
if err != nil {
- if output.Quiet {
- fmt.Printf("%d: %s\n", id, HumanReadableError(err))
- } else {
- output.Rows[i] = ModelStatusOutputRow{Id: id, Message: HumanReadableError(err)}
+ if output.Unbuffered {
+ fmt.Printf("%v: %s\n", id, HumanReadableError(err))
}
+ output.Rows[i] = ModelStatusOutputRow{Id: id, Message: HumanReadableError(err)}
} else {
- if output.Quiet {
+ if output.Unbuffered && final {
fmt.Println(id)
- } else {
- output.Rows[i] = ModelStatusOutputRow{Id: id, Message: status}
}
+ output.Rows[i] = ModelStatusOutputRow{Id: id, Message: status}
}
}
@@ -254,17 +290,29 @@
fields[fieldName] = proto_value
}
- modelStatusOutput := InitModelStatusOutput(options.Quiet, len(models))
+ modelStatusOutput := InitModelStatusOutput(options.Unbuffered, len(models))
for i, model := range models {
id := model.GetFieldByName("id").(int32)
fields["id"] = id
err := UpdateModel(conn, descriptor, modelName, fields)
- UpdateModelStatusOutput(&modelStatusOutput, i, id, "Updated", err)
+ UpdateModelStatusOutput(&modelStatusOutput, i, id, "Updated", err, !options.Sync)
}
- if !options.Quiet {
- FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_UPDATE_FORMAT, "", modelStatusOutput.Rows)
+ if options.Sync {
+ for i, model := range models {
+ id := model.GetFieldByName("id").(int32)
+ if modelStatusOutput.Rows[i].Message == "Updated" {
+ conditional_printf(!options.Quiet, "Wait for sync: %d ", id)
+ conn, _, err = GetModelWithRetry(conn, descriptor, modelName, id, GM_UNTIL_ENACTED|Ternary_uint32(options.Quiet, GM_QUIET, 0))
+ conditional_printf(!options.Quiet, "\n")
+ UpdateModelStatusOutput(&modelStatusOutput, i, id, "Enacted", err, true)
+ }
+ }
+ }
+
+ if !options.Unbuffered {
+ FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_UPDATE_FORMAT, DEFAULT_UPDATE_FORMAT, modelStatusOutput.Rows)
}
return nil
@@ -285,7 +333,7 @@
if (len(options.IDArgs.ID) == 0 && len(options.Filter) == 0) ||
(len(options.IDArgs.ID) != 0 && len(options.Filter) != 0) {
- return fmt.Errorf("Use either an ID or a --filter to specify which models to update")
+ return fmt.Errorf("Use either an ID or a --filter to specify which models to delete")
}
queries, err := CommaSeparatedQueryToMap(options.Filter, true)
@@ -317,14 +365,133 @@
}
}
- modelStatusOutput := InitModelStatusOutput(options.Quiet, len(ids))
+ modelStatusOutput := InitModelStatusOutput(options.Unbuffered, len(ids))
for i, id := range ids {
err = DeleteModel(conn, descriptor, modelName, id)
- UpdateModelStatusOutput(&modelStatusOutput, i, id, "Deleted", err)
+ UpdateModelStatusOutput(&modelStatusOutput, i, id, "Deleted", err, true)
}
- if !options.Quiet {
- FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_DELETE_FORMAT, "", modelStatusOutput.Rows)
+ if !options.Unbuffered {
+ FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_DELETE_FORMAT, DEFAULT_DELETE_FORMAT, modelStatusOutput.Rows)
+ }
+
+ return nil
+}
+
+func (options *ModelCreate) Execute(args []string) error {
+ conn, descriptor, err := InitReflectionClient()
+ if err != nil {
+ return err
+ }
+
+ defer conn.Close()
+
+ err = CheckModelName(descriptor, string(options.Args.ModelName))
+ if err != nil {
+ return err
+ }
+
+ updates, err := CommaSeparatedQueryToMap(options.SetFields, true)
+ if err != nil {
+ return err
+ }
+
+ modelName := string(options.Args.ModelName)
+
+ fields := make(map[string]interface{})
+
+ if len(options.SetJSON) > 0 {
+ fields["_json"] = []byte(options.SetJSON)
+ }
+
+ for fieldName, value := range updates {
+ value = value[1:]
+ proto_value, err := TypeConvert(descriptor, modelName, fieldName, value)
+ if err != nil {
+ return err
+ }
+ fields[fieldName] = proto_value
+ }
+
+ modelStatusOutput := InitModelStatusOutput(options.Unbuffered, 1)
+
+ err = CreateModel(conn, descriptor, modelName, fields)
+ UpdateModelStatusOutput(&modelStatusOutput, 0, fields["id"], "Created", err, !options.Sync)
+
+ if options.Sync {
+ if modelStatusOutput.Rows[0].Message == "Created" {
+ id := fields["id"].(int32)
+ conditional_printf(!options.Quiet, "Wait for sync: %d ", id)
+ conn, _, err = GetModelWithRetry(conn, descriptor, modelName, id, GM_UNTIL_ENACTED|Ternary_uint32(options.Quiet, GM_QUIET, 0))
+ conditional_printf(!options.Quiet, "\n")
+ UpdateModelStatusOutput(&modelStatusOutput, 0, id, "Enacted", err, true)
+ }
+ }
+
+ if !options.Unbuffered {
+ FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_CREATE_FORMAT, DEFAULT_CREATE_FORMAT, modelStatusOutput.Rows)
+ }
+
+ return nil
+}
+
+func (options *ModelSync) Execute(args []string) error {
+ conn, descriptor, err := InitReflectionClient()
+ if err != nil {
+ return err
+ }
+
+ defer conn.Close()
+
+ err = CheckModelName(descriptor, string(options.Args.ModelName))
+ if err != nil {
+ return err
+ }
+
+ if (len(options.IDArgs.ID) == 0 && len(options.Filter) == 0) ||
+ (len(options.IDArgs.ID) != 0 && len(options.Filter) != 0) {
+ return fmt.Errorf("Use either an ID or a --filter to specify which models to sync")
+ }
+
+ queries, err := CommaSeparatedQueryToMap(options.Filter, true)
+ if err != nil {
+ return err
+ }
+
+ modelName := string(options.Args.ModelName)
+
+ var ids []int32
+
+ if len(options.IDArgs.ID) > 0 {
+ ids = options.IDArgs.ID
+ } else {
+ models, err := ListOrFilterModels(conn, descriptor, modelName, queries)
+ if err != nil {
+ return err
+ }
+ ids = make([]int32, len(models))
+ for i, model := range models {
+ ids[i] = model.GetFieldByName("id").(int32)
+ }
+ if len(ids) == 0 {
+ return fmt.Errorf("Filter matches no objects")
+ } else if len(ids) > 1 {
+ if !Confirmf("Filter matches %d objects. Continue [y/n] ? ", len(models)) {
+ return fmt.Errorf("Aborted by user")
+ }
+ }
+ }
+
+ modelStatusOutput := InitModelStatusOutput(options.Unbuffered, len(ids))
+ for i, id := range ids {
+ conditional_printf(!options.Quiet, "Wait for sync: %d ", id)
+ conn, _, err = GetModelWithRetry(conn, descriptor, modelName, id, GM_UNTIL_ENACTED|Ternary_uint32(options.Quiet, GM_QUIET, 0))
+ conditional_printf(!options.Quiet, "\n")
+ UpdateModelStatusOutput(&modelStatusOutput, i, id, "Enacted", err, true)
+ }
+
+ if !options.Unbuffered {
+ FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_SYNC_FORMAT, DEFAULT_SYNC_FORMAT, modelStatusOutput.Rows)
}
return nil