From 5884471c92119f99323793c91d6f6f0483fec6be Mon Sep 17 00:00:00 2001 From: Grant Arnold Date: Mon, 1 Apr 2024 22:50:27 +0000 Subject: [PATCH 1/7] Add connections command to get connections. --- cmd/connections.go | 212 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 cmd/connections.go diff --git a/cmd/connections.go b/cmd/connections.go new file mode 100644 index 0000000..3a0b1e3 --- /dev/null +++ b/cmd/connections.go @@ -0,0 +1,212 @@ +package cmd + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "strings" + + "github.com/jedib0t/go-pretty/v6/table" + "github.com/spf13/cobra" +) + +type connectionsFlags struct { + name string + typeConnection []string + excludedType []string + category []string + outputType string + noHeaders bool +} + +type FMEFlowConnections struct { + Items []Connection `json:"items"` + TotalCount int `json:"totalCount"` + Limit int `json:"limit"` + Offset int `json:"offset"` +} + +type Connection struct { + Name string `json:"name"` + Category string `json:"category"` + Type string `json:"type"` + Owner string `json:"owner"` + Shareable bool `json:"shareable"` + Parameters map[string]interface{} `json:"parameters"` +} + +func newConnectionsCmd() *cobra.Command { + f := connectionsFlags{} + cmd := &cobra.Command{ + Use: "connections", + Short: "Lists connections on FME Flow", + Long: "Lists connections on FME Flow. Pass in a name to retrieve information on a single project.", + Example: ` + # List all projects + fmeflow projects + + # List all projects owned by the user admin + fmeflow projects --owner admin`, + Args: NoArgs, + RunE: connectionsRun(&f), + } + + cmd.Flags().StringVar(&f.name, "name", "", "Return a single project with the given name.") + cmd.Flags().StringArrayVar(&f.typeConnection, "type", []string{}, "The types of connections to return. Can be passed in multiple times") + cmd.Flags().StringArrayVar(&f.excludedType, "excluded-type", []string{}, "The types of connections to exclude. Can be passed in multiple times") + cmd.Flags().StringArrayVar(&f.category, "category", []string{}, "The categories of connections to return. Can be passed in multiple times") + cmd.Flags().StringVarP(&f.outputType, "output", "o", "table", "Specify the output type. Should be one of table, json, or custom-columns") + cmd.Flags().BoolVar(&f.noHeaders, "no-headers", false, "Don't print column headers") + cmd.AddCommand(newProjectDownloadCmd()) + cmd.AddCommand(newProjectUploadCmd()) + + return cmd +} +func connectionsRun(f *connectionsFlags) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + // --json overrides --output + if jsonOutput { + f.outputType = "json" + } + + client := &http.Client{} + + url := "/fmeapiv4/connections" + if f.name != "" { + url = url + "/" + f.name + } + + request, err := buildFmeFlowRequest(url, "GET", nil) + if err != nil { + return err + } + + q := request.URL.Query() + + for _, t := range f.typeConnection { + q.Add("types", t) + } + + for _, t := range f.excludedType { + q.Add("excludedTypes", t) + } + + for _, c := range f.category { + q.Add("categories", c) + } + + //q.Add("filterString", f.owner) + //q.Add("filterProperties", "owner") + request.URL.RawQuery = q.Encode() + + response, err := client.Do(&request) + if err != nil { + return err + } else if response.StatusCode != http.StatusOK { + responseData, err := io.ReadAll(response.Body) + if err == nil { + var responseMessage Message + if err := json.Unmarshal(responseData, &responseMessage); err == nil { + + // if json output is requested, output the JSON to stdout before erroring + if jsonOutput { + prettyJSON, err := prettyPrintJSON(responseData) + if err == nil { + fmt.Fprintln(cmd.OutOrStdout(), prettyJSON) + } else { + return errors.New(response.Status) + } + } + return errors.New(responseMessage.Message) + } else { + return errors.New(response.Status) + } + } else { + return errors.New(response.Status) + } + } + + // marshal into struct + var result FMEFlowConnections + + responseData, err := io.ReadAll(response.Body) + if err != nil { + return err + } + + if f.name == "" { + // if no name specified, request will return the full struct + if err := json.Unmarshal(responseData, &result); err != nil { + return err + } + } else { + var connectionStruct Connection + + if err := json.Unmarshal(responseData, &connectionStruct); err != nil { + return err + } + result.TotalCount = 1 + result.Items = append(result.Items, connectionStruct) + } + + if f.outputType == "table" { + + t := table.NewWriter() + t.SetStyle(defaultStyle) + + t.AppendHeader(table.Row{"Name", "Type", "Category"}) + + for _, element := range result.Items { + t.AppendRow(table.Row{element.Name, element.Type, element.Category}) + } + if f.noHeaders { + t.ResetHeaders() + } + fmt.Fprintln(cmd.OutOrStdout(), t.Render()) + + } else if f.outputType == "json" { + prettyJSON, err := prettyPrintJSON(responseData) + if err != nil { + return err + } + fmt.Fprintln(cmd.OutOrStdout(), prettyJSON) + } else if strings.HasPrefix(f.outputType, "custom-columns") { + // parse the columns and json queries + columnsString := "" + if strings.HasPrefix(f.outputType, "custom-columns=") { + columnsString = f.outputType[len("custom-columns="):] + } + if len(columnsString) == 0 { + return errors.New("custom-columns format specified but no custom columns given") + } + + // we have to marshal the Items array, then create an array of marshalled items + // to pass to the creation of the table. + marshalledItems := [][]byte{} + for _, element := range result.Items { + mJson, err := json.Marshal(element) + if err != nil { + return err + } + marshalledItems = append(marshalledItems, mJson) + } + + columnsInput := strings.Split(columnsString, ",") + t, err := createTableFromCustomColumns(marshalledItems, columnsInput) + if err != nil { + return err + } + if f.noHeaders { + t.ResetHeaders() + } + fmt.Fprintln(cmd.OutOrStdout(), t.Render()) + + } else { + return errors.New("invalid output format specified") + } + + return nil + } +} From fe814225be5e5d4af958bcd925640f804bc1b9c3 Mon Sep 17 00:00:00 2001 From: Grant Arnold Date: Tue, 2 Apr 2024 18:03:56 +0000 Subject: [PATCH 2/7] Add connections command to main command. Allow missing jsonpath values. --- cmd/custom-columns.go | 1 + cmd/root.go | 1 + docs/custom-columns.md | 2 ++ 3 files changed, 4 insertions(+) diff --git a/cmd/custom-columns.go b/cmd/custom-columns.go index 03f0c80..1b1f3c2 100644 --- a/cmd/custom-columns.go +++ b/cmd/custom-columns.go @@ -72,6 +72,7 @@ func createTableFromCustomColumns(jsonItems [][]byte, columnsInput []string) (ta json.Unmarshal(element, &v) j := jsonpath.New("Parser") + j.AllowMissingKeys(true) if err := j.Parse(columnQuery); err != nil { return nil, err } diff --git a/cmd/root.go b/cmd/root.go index 78209c6..18fd1ec 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -54,6 +54,7 @@ func NewRootCommand() *cobra.Command { cmds.AddCommand(newWorkspaceCmd()) cmds.AddCommand(newProjectsCmd()) cmds.AddCommand(newDeploymentParametersCmd()) + cmds.AddCommand(newConnectionsCmd()) cmds.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error { cmd.PrintErrln(err) cmd.PrintErrln(cmd.UsageString()) diff --git a/docs/custom-columns.md b/docs/custom-columns.md index 84fe153..4c20580 100644 --- a/docs/custom-columns.md +++ b/docs/custom-columns.md @@ -10,6 +10,8 @@ HEADERNAME:.jsonPathForValue ``` `HEADERNAME` will be the heading of the column in the table, and `.jsonPathForValue` will be the JSONPath expression to retrieve the data for that column from the returned JSON. This allows you to dynamically set column values to whatever data you need from the json. +If a specified json query references a field that is missing on the json that it is querying, it will return an empty value instead of erroring. + ### JSONPath JSONPath is a query language for JSON. The implementation of it in the FME Server CLI is heavily based on the implementation of it for the `kubectl` CLI for Kubernetes. More detailed documentation on how to use it can be found in the [Kubernetes documentation](https://kubernetes.io/docs/reference/kubectl/jsonpath/) for things that are not covered here. From 24cddde0d70cfe91c2d52bcf15b801ef631f2f2a Mon Sep 17 00:00:00 2001 From: Grant Arnold Date: Fri, 5 Apr 2024 00:20:21 +0000 Subject: [PATCH 3/7] Add connection Create/Update/Delete --- cmd/connections.go | 7 +- cmd/connections_create.go | 154 ++++++++++++++++++++++++++ cmd/connections_create_test.go | 75 +++++++++++++ cmd/connections_delete.go | 135 +++++++++++++++++++++++ cmd/connections_delete_test.go | 69 ++++++++++++ cmd/connections_test.go | 106 ++++++++++++++++++ cmd/connections_update.go | 190 +++++++++++++++++++++++++++++++++ cmd/connections_update_test.go | 87 +++++++++++++++ cmd/requestfile_test.go | 2 +- 9 files changed, 820 insertions(+), 5 deletions(-) create mode 100644 cmd/connections_create.go create mode 100644 cmd/connections_create_test.go create mode 100644 cmd/connections_delete.go create mode 100644 cmd/connections_delete_test.go create mode 100644 cmd/connections_test.go create mode 100644 cmd/connections_update.go create mode 100644 cmd/connections_update_test.go diff --git a/cmd/connections.go b/cmd/connections.go index 3a0b1e3..acaaf68 100644 --- a/cmd/connections.go +++ b/cmd/connections.go @@ -59,8 +59,9 @@ func newConnectionsCmd() *cobra.Command { cmd.Flags().StringArrayVar(&f.category, "category", []string{}, "The categories of connections to return. Can be passed in multiple times") cmd.Flags().StringVarP(&f.outputType, "output", "o", "table", "Specify the output type. Should be one of table, json, or custom-columns") cmd.Flags().BoolVar(&f.noHeaders, "no-headers", false, "Don't print column headers") - cmd.AddCommand(newProjectDownloadCmd()) - cmd.AddCommand(newProjectUploadCmd()) + cmd.AddCommand(newConnectionCreateCmd()) + cmd.AddCommand(newConnectionUpdateCmd()) + cmd.AddCommand(newConnectionDeleteCmd()) return cmd } @@ -97,8 +98,6 @@ func connectionsRun(f *connectionsFlags) func(cmd *cobra.Command, args []string) q.Add("categories", c) } - //q.Add("filterString", f.owner) - //q.Add("filterProperties", "owner") request.URL.RawQuery = q.Encode() response, err := client.Do(&request) diff --git a/cmd/connections_create.go b/cmd/connections_create.go new file mode 100644 index 0000000..a87a785 --- /dev/null +++ b/cmd/connections_create.go @@ -0,0 +1,154 @@ +package cmd + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "strings" + + "github.com/spf13/cobra" +) + +type NewConnection struct { + Category string `json:"category"` + Name string `json:"name"` + Type string `json:"type"` + AuthenticationMethod string `json:"authenticationMethod,omitempty"` + Username string `json:"username"` + Password string `json:"password"` + Parameters map[string]interface{} `json:"parameters,omitempty"` +} + +type ConnectionCreateFlags struct { + connectionType string + name string + category string + authenticationMethod string + username string + password string + parameter []string +} + +type ConnectionCreateMessage struct { + Message string `json:"message"` + Details map[string]interface{} `json:"details"` +} + +func newConnectionCreateCmd() *cobra.Command { + f := ConnectionCreateFlags{} + cmd := &cobra.Command{ + Use: "create", + Short: "Create a connection", + Long: `Create a connection.`, + Example: ` + Examples: + # Create a connection with the name "myConnection" and the category "PostgreSQL" and the type "database" with username "myUser" and password "myPassword" + fmeflow connections create --name myConnection --category database --type PostgreSQL --username myUser --password myPassword +`, + + Args: NoArgs, + RunE: connectionCreateRun(&f), + } + + cmd.Flags().StringVar(&f.name, "name", "", "Name of the connection to create.") + cmd.Flags().StringVar(&f.category, "category", "", "Category of the connection to create. Typically it is one of: \"basic\", \"database\", \"token\", \"oauthV1\", \"oauthV2\".") + cmd.Flags().StringVar(&f.connectionType, "type", "", "Type of connection.") + cmd.Flags().StringVar(&f.authenticationMethod, "authenticationMethod", "", "Authentication method of the connection to create.") + cmd.Flags().StringVar(&f.username, "username", "", "Username of the connection to create.") + cmd.Flags().StringVar(&f.password, "password", "", "Password of the connection to create.") + cmd.Flags().StringArrayVar(&f.parameter, "parameter", []string{}, "Parameters of the connection to create. Must be of the form name=value. Can be specified multiple times.") + + cmd.MarkFlagRequired("name") + cmd.MarkFlagRequired("category") + return cmd +} + +func connectionCreateRun(f *ConnectionCreateFlags) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + + // set up http + client := &http.Client{} + + var newConnection NewConnection + newConnection.Name = f.name + newConnection.Category = f.category + newConnection.Type = f.connectionType + if f.authenticationMethod != "" { + newConnection.AuthenticationMethod = f.authenticationMethod + } + newConnection.Username = f.username + newConnection.Password = f.password + + if len(f.parameter) != 0 { + newConnection.Parameters = make(map[string]interface{}) + } + for _, param := range f.parameter { + parts := strings.Split(param, "=") + if len(parts) != 2 { + return errors.New("parameter must be in the format name=value") + } + newConnection.Parameters[parts[0]] = parts[1] + } + + jsonData, err := json.Marshal(newConnection) + if err != nil { + return err + } + + request, err := buildFmeFlowRequest("/fmeapiv4/connections", "POST", bytes.NewBuffer(jsonData)) + if err != nil { + return err + } + + q := request.URL.Query() + q.Add("encoded", "false") + + request.Header.Add("Content-Type", "application/json") + + response, err := client.Do(&request) + if err != nil { + return err + } else if response.StatusCode != http.StatusCreated { + // attempt to parse the body into JSON as there could be a valuable message in there + // if fail, just output the status code + responseData, err := io.ReadAll(response.Body) + if err == nil { + var responseMessage ConnectionCreateMessage + if err := json.Unmarshal(responseData, &responseMessage); err == nil { + + // if json output is requested, output the JSON to stdout before erroring + if jsonOutput { + prettyJSON, err := prettyPrintJSON(responseData) + if err == nil { + fmt.Fprintln(cmd.OutOrStdout(), prettyJSON) + } else { + return errors.New(response.Status) + } + } else { + errorMessage := responseMessage.Message + for key, value := range responseMessage.Details { + errorMessage += fmt.Sprintf("\n%s: %v", key, value) + } + return errors.New(errorMessage) + } + + } else { + return errors.New(response.Status) + } + } else { + return errors.New(response.Status) + } + } else { + if !jsonOutput { + fmt.Fprintln(cmd.OutOrStdout(), "Connection successfully created.") + } else { + fmt.Fprintln(cmd.OutOrStdout(), "{}") + } + } + + return nil + } +} diff --git a/cmd/connections_create_test.go b/cmd/connections_create_test.go new file mode 100644 index 0000000..4f550b2 --- /dev/null +++ b/cmd/connections_create_test.go @@ -0,0 +1,75 @@ +package cmd + +import ( + "net/http" + "testing" +) + +func TestConnectionsCreate(t *testing.T) { + responseOracleMissing := `{ + "message": "The connection parameter(s) are required but are missing or empty: CONNECTION_MODE, DATASET, WALLET_PATH, SELECTED_SERVICE." + }` + + responseDatabaseTypeMissing := `{ + "message": "Specified database type does not exist." + }` + + responseParameterValidationFailed := `{ + "message": "Parameter Validation Failed", + "details": { + "authenticationMethod": "Connection authentication method must be supplied", + "type": "must not be blank" + } + }` + + cases := []testCase{ + { + name: "unknown flag", + statusCode: http.StatusOK, + args: []string{"connections", "--badflag"}, + wantErrOutputRegex: "unknown flag: --badflag", + }, + { + name: "500 bad status code", + statusCode: http.StatusInternalServerError, + wantErrText: "500 Internal Server Error", + args: []string{"connections"}, + }, + { + name: "create connection", + statusCode: http.StatusCreated, + args: []string{"connections", "create", "--name", "test123aa", "--category", "database", "--type", "PostgreSQL", "--username", "test", "--password", "test", "--parameter", "HOST=a", "--parameter", "PORT=5432", "--parameter", "DATASET=dbname", "--parameter", "USER_NAME=a", "--parameter", "SSL_OPTIONS=a", "--parameter", "SSLMODE=prefer"}, + wantOutputRegex: "^[\\s]*Connection successfully created.[\\s]*$", + }, + { + name: "create connection json output", + statusCode: http.StatusCreated, + args: []string{"connections", "create", "--name", "test123aa", "--category", "database", "--type", "PostgreSQL", "--username", "test", "--password", "test", "--parameter", "HOST=a", "--parameter", "PORT=5432", "--parameter", "DATASET=dbname", "--parameter", "USER_NAME=a", "--parameter", "SSL_OPTIONS=a", "--parameter", "SSLMODE=prefer", "--json"}, + wantOutputJson: "{}", + }, + { + name: "create connection missing oracle parameters", + statusCode: http.StatusBadRequest, + args: []string{"connections", "create", "--name", "test123aa", "--category", "database", "--type", "Oracle"}, + body: responseOracleMissing, + wantErrText: "The connection parameter(s) are required but are missing or empty: CONNECTION_MODE, DATASET, WALLET_PATH, SELECTED_SERVICE.", + }, + { + name: "create connection missing database type", + statusCode: http.StatusBadRequest, + args: []string{"connections", "create", "--name", "test123aa", "--category", "database", "--type", "test"}, + body: responseDatabaseTypeMissing, + wantErrText: "Specified database type does not exist.", + }, + { + name: "create connection missing parameters", + statusCode: http.StatusBadRequest, + args: []string{"connections", "create", "--name", "test123aa", "--category", "basic"}, + body: responseParameterValidationFailed, + wantErrText: "Parameter Validation Failed\nauthenticationMethod: Connection authentication method must be supplied\ntype: must not be blank", + }, + } + + runTests(cases, t) + +} diff --git a/cmd/connections_delete.go b/cmd/connections_delete.go new file mode 100644 index 0000000..abf4603 --- /dev/null +++ b/cmd/connections_delete.go @@ -0,0 +1,135 @@ +package cmd + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + + "github.com/AlecAivazis/survey/v2" + "github.com/spf13/cobra" +) + +type ConnectionDeleteFlags struct { + name string + noprompt bool +} + +func newConnectionDeleteCmd() *cobra.Command { + f := ConnectionDeleteFlags{} + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete a connection", + Long: `Delete a connection.`, + Example: ` + Examples: + # Delete a connection with the name "myConnection" + fmeflow connections delete --name myConnection +`, + + Args: NoArgs, + RunE: connectionDeleteRun(&f), + } + + cmd.Flags().StringVar(&f.name, "name", "", "Name of the connection to delete.") + cmd.Flags().BoolVarP(&f.noprompt, "no-prompt", "y", false, "Description of the new repository.") + + cmd.MarkFlagRequired("name") + return cmd +} + +func connectionDeleteRun(f *ConnectionDeleteFlags) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + + // set up http + client := &http.Client{} + + // check if deployment parameter exists first and error if it does not + request, err := buildFmeFlowRequest("/fmeapiv4/connections/"+f.name, "GET", nil) + if err != nil { + return err + } + // send the request + response, err := client.Do(&request) + if err != nil { + return err + } else if response.StatusCode != http.StatusOK { + // if we didn't get a 200 OK, then the deployment parameter does not exist + // get the JSON response and throw a new error using the message + responseData, err := io.ReadAll(response.Body) + if err == nil { + var responseMessage Message + if err := json.Unmarshal(responseData, &responseMessage); err == nil { + // if json output is requested, output the JSON to stdout before erroring + if jsonOutput { + prettyJSON, err := prettyPrintJSON(responseData) + if err == nil { + fmt.Fprintln(cmd.OutOrStdout(), prettyJSON) + } else { + return errors.New(response.Status) + } + } + return errors.New(responseMessage.Message) + } else { + return errors.New(response.Status) + } + } + } + + // the parameter exists. Confirm deletion. + if !f.noprompt { + // prompt to confirm deletion + confirm := false + promptUser := &survey.Confirm{ + Message: "Are you sure you want to delete the deployment parameter " + f.name + "?", + } + survey.AskOne(promptUser, &confirm) + if !confirm { + return nil + } + } + + // get the current values of the connection we are going to update + url := "/fmeapiv4/connections/" + f.name + request, err = buildFmeFlowRequest(url, "DELETE", nil) + if err != nil { + return err + } + + response, err = client.Do(&request) + if err != nil { + return err + } else if response.StatusCode != http.StatusNoContent { + responseData, err := io.ReadAll(response.Body) + if err == nil { + var responseMessage Message + if err := json.Unmarshal(responseData, &responseMessage); err == nil { + + // if json output is requested, output the JSON to stdout before erroring + if jsonOutput { + prettyJSON, err := prettyPrintJSON(responseData) + if err == nil { + fmt.Fprintln(cmd.OutOrStdout(), prettyJSON) + } else { + return errors.New(response.Status) + } + } + return errors.New(responseMessage.Message) + } else { + return errors.New(response.Status) + } + } else { + return errors.New(response.Status) + } + } + + if !jsonOutput { + fmt.Fprintln(cmd.OutOrStdout(), "Connection successfully deleted.") + } else { + fmt.Fprintln(cmd.OutOrStdout(), "{}") + } + + return nil + } +} diff --git a/cmd/connections_delete_test.go b/cmd/connections_delete_test.go new file mode 100644 index 0000000..85520b5 --- /dev/null +++ b/cmd/connections_delete_test.go @@ -0,0 +1,69 @@ +package cmd + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestConnectionsDelete(t *testing.T) { + customHttpServerHandler := func(w http.ResponseWriter, r *http.Request) { + + if r.Method == "GET" { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte("")) + require.NoError(t, err) + + } + if r.Method == "DELETE" { + w.WriteHeader(http.StatusNoContent) + _, err := w.Write([]byte("")) + require.NoError(t, err) + + } + + } + + paramMissingBody := `{ + "message": "Unauthorized request by user admin due to lack of proper permissions or the object does not exist." + }` + + cases := []testCase{ + { + name: "unknown flag", + statusCode: http.StatusOK, + args: []string{"connections", "delete", "--name", "myConn", "--badflag"}, + wantErrOutputRegex: "unknown flag: --badflag", + }, + { + name: "500 bad status code", + statusCode: http.StatusInternalServerError, + wantErrText: "500 Internal Server Error", + args: []string{"connections", "delete", "--name", "myConn", "--no-prompt"}, + }, + { + name: "missing flag", + wantErrText: "required flag(s) \"name\" not set", + args: []string{"connections", "delete"}, + }, + { + name: "delete parameter", + statusCode: http.StatusNoContent, + args: []string{"connections", "delete", "--name", "myConn", "--no-prompt"}, + wantOutputRegex: "^Connection successfully deleted.[\\s]*$", + httpServer: httptest.NewServer(http.HandlerFunc(customHttpServerHandler)), + }, + { + name: "delete parameter not found", + statusCode: http.StatusNotFound, + body: paramMissingBody, + args: []string{"connections", "delete", "--name", "myConn", "--no-prompt"}, + wantErrText: "Unauthorized request by user admin due to lack of proper permissions or the object does not exist.", + }, + } + + runTests(cases, t) + +} diff --git a/cmd/connections_test.go b/cmd/connections_test.go new file mode 100644 index 0000000..a963330 --- /dev/null +++ b/cmd/connections_test.go @@ -0,0 +1,106 @@ +package cmd + +import ( + "net/http" + "testing" +) + +func TestConnections(t *testing.T) { + response := `{ + "items": [ + { + "name": "Google Drive Named Connection", + "category": "oauthV2", + "type": "Google Drive", + "owner": "admin", + "shareable": true + }, + { + "name": "PostGIS 3.3 Testsuite", + "category": "database", + "type": "PostgreSQL", + "owner": "admin", + "shareable": true, + "parameters": { + "SSL_OPTIONS": "", + "PORT": "5434", + "CLIENT_PRIV_KEY": "", + "SSLMODE": "prefer", + "HOST": "somehost", + "CA_CERTIFICATE": "", + "DATASET": "testsuitegis", + "USER_NAME": "testsuite", + "CLIENT_CERTIFICATE": "" + } + } + ], + "totalCount": 2, + "limit": 100, + "offset": 0 + }` + + responseSingle := `{ + "name": "PostGIS 3.3 Testsuite", + "category": "database", + "type": "PostgreSQL", + "owner": "admin", + "shareable": true, + "parameters": { + "SSL_OPTIONS": "", + "PORT": "5434", + "CLIENT_PRIV_KEY": "", + "SSLMODE": "prefer", + "HOST": "somehost", + "CA_CERTIFICATE": "", + "DATASET": "testsuitegis", + "USER_NAME": "testsuite", + "CLIENT_CERTIFICATE": "" + } + }` + + cases := []testCase{ + { + name: "unknown flag", + statusCode: http.StatusOK, + args: []string{"connections", "--badflag"}, + wantErrOutputRegex: "unknown flag: --badflag", + }, + { + name: "500 bad status code", + statusCode: http.StatusInternalServerError, + wantErrText: "500 Internal Server Error", + args: []string{"connections"}, + }, + { + name: "get connections table output", + statusCode: http.StatusOK, + args: []string{"connections"}, + body: response, + wantOutputRegex: "^[\\s]*NAME[\\s]*TYPE[\\s]*CATEGORY[\\s]*Google Drive Named Connection[\\s]*Google Drive[\\s]*oauthV2[\\s]*PostGIS 3.3 Testsuite[\\s]*PostgreSQL[\\s]*database[\\s]*$", + }, + { + name: "get connections json output", + statusCode: http.StatusOK, + args: []string{"connections", "--json"}, + body: response, + wantOutputJson: response, + }, + { + name: "get single connection", + statusCode: http.StatusOK, + body: responseSingle, + args: []string{"connections", "--name", "PostGIS 3.3 Testsuite"}, + wantOutputRegex: "^[\\s]*NAME[\\s]*TYPE[\\s]*CATEGORY[\\s]*PostGIS 3.3 Testsuite[\\s]*PostgreSQL[\\s]*database[\\s]*$", + }, + { + name: "get connections custom columns", + statusCode: http.StatusOK, + args: []string{"repositories", "--output=custom-columns=NAME:.name"}, + body: response, + wantOutputRegex: "^[\\s]*NAME[\\s]*Google Drive Named Connection[\\s]*PostGIS 3.3 Testsuite[\\s]*$", + }, + } + + runTests(cases, t) + +} diff --git a/cmd/connections_update.go b/cmd/connections_update.go new file mode 100644 index 0000000..497b0da --- /dev/null +++ b/cmd/connections_update.go @@ -0,0 +1,190 @@ +package cmd + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "strings" + + "github.com/spf13/cobra" +) + +type UpdateConnection struct { + Category string `json:"category"` + AuthenticationMethod string `json:"authenticationMethod,omitempty"` + Username string `json:"username"` + Password string `json:"password"` + Parameters map[string]interface{} `json:"parameters,omitempty"` +} + +type ConnectionUpdateFlags struct { + name string + authenticationMethod string + username string + password string + parameter []string +} + +func newConnectionUpdateCmd() *cobra.Command { + f := ConnectionUpdateFlags{} + cmd := &cobra.Command{ + Use: "update", + Short: "Update a connection", + Long: `Update a connection.`, + Example: ` + Examples: + # Update a connection with the name "myConnection" and the category "PostgreSQL" and the type "database" with username "myUser" and password "myPassword" + fmeflow connections update --name myConnection --category database --username myUser --password myPassword +`, + + Args: NoArgs, + RunE: connectionUpdateRun(&f), + } + + cmd.Flags().StringVar(&f.name, "name", "", "Name of the connection to update.") + cmd.Flags().StringVar(&f.authenticationMethod, "authenticationMethod", "", "Authentication method of the connection to update.") + cmd.Flags().StringVar(&f.username, "username", "", "Username of the connection to update.") + cmd.Flags().StringVar(&f.password, "password", "", "Password of the connection to update.") + cmd.Flags().StringArrayVar(&f.parameter, "parameter", []string{}, "Parameters of the connection to update. Must be of the form name=value. Can be specified multiple times.") + + cmd.MarkFlagRequired("name") + return cmd +} + +func connectionUpdateRun(f *ConnectionUpdateFlags) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + + // set up http + client := &http.Client{} + + // get the current values of the connection we are going to update + url := "/fmeapiv4/connections/" + f.name + request, err := buildFmeFlowRequest(url, "GET", nil) + if err != nil { + return err + } + + response, err := client.Do(&request) + if err != nil { + return err + } else if response.StatusCode != http.StatusOK { + responseData, err := io.ReadAll(response.Body) + if err == nil { + var responseMessage Message + if err := json.Unmarshal(responseData, &responseMessage); err == nil { + + // if json output is requested, output the JSON to stdout before erroring + if jsonOutput { + prettyJSON, err := prettyPrintJSON(responseData) + if err == nil { + fmt.Fprintln(cmd.OutOrStdout(), prettyJSON) + } else { + return errors.New(response.Status) + } + } + return errors.New(responseMessage.Message) + } else { + return errors.New(response.Status) + } + } else { + return errors.New(response.Status) + } + } + + responseData, err := io.ReadAll(response.Body) + if err != nil { + return err + } + + var existingConnectionStruct Connection + + if err := json.Unmarshal(responseData, &existingConnectionStruct); err != nil { + return err + } + + // build the struct for the update, filling in missing fields with the existing connection's values + + var updateConnectionStruct UpdateConnection + + // category can't be updated + updateConnectionStruct.Category = existingConnectionStruct.Category + + if f.authenticationMethod != "" { + updateConnectionStruct.AuthenticationMethod = f.authenticationMethod + } + if f.username != "" { + updateConnectionStruct.Username = f.username + } + if f.password != "" { + updateConnectionStruct.Password = f.password + } + + // copy over existing parameters + updateConnectionStruct.Parameters = existingConnectionStruct.Parameters + + // set new parameters + for _, param := range f.parameter { + parts := strings.Split(param, "=") + if len(parts) != 2 { + return errors.New("parameter must be in the format name=value") + } + updateConnectionStruct.Parameters[parts[0]] = parts[1] + } + + jsonData, err := json.Marshal(updateConnectionStruct) + if err != nil { + return err + } + + request, err = buildFmeFlowRequest(url, "PUT", bytes.NewBuffer(jsonData)) + if err != nil { + return err + } + + request.Header.Add("Content-Type", "application/json") + + response, err = client.Do(&request) + if err != nil { + return err + } else if response.StatusCode != http.StatusNoContent { + // attempt to parse the body into JSON as there could be a valuable message in there + // if fail, just output the status code + responseData, err := io.ReadAll(response.Body) + if err == nil { + var responseMessage Message + if err := json.Unmarshal(responseData, &responseMessage); err == nil { + + // if json output is requested, output the JSON to stdout before erroring + if jsonOutput { + prettyJSON, err := prettyPrintJSON(responseData) + if err == nil { + fmt.Fprintln(cmd.OutOrStdout(), prettyJSON) + } else { + return errors.New(response.Status) + } + } + errorMessage := responseMessage.Message + if response.StatusCode == http.StatusBadRequest { + errorMessage = errorMessage + ". Check that the specified category is correct." + } + return errors.New(errorMessage) + } else { + return errors.New(response.Status) + } + } else { + return errors.New(response.Status) + } + } + + if !jsonOutput { + fmt.Fprintln(cmd.OutOrStdout(), "Connection successfully updated.") + } else { + fmt.Fprintln(cmd.OutOrStdout(), "{}") + } + + return nil + } +} diff --git a/cmd/connections_update_test.go b/cmd/connections_update_test.go new file mode 100644 index 0000000..7285c7e --- /dev/null +++ b/cmd/connections_update_test.go @@ -0,0 +1,87 @@ +package cmd + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestConnectionsUpdate(t *testing.T) { + responseGet := `{ + "name": "PostGIS 3.3 Testsuite", + "category": "database", + "type": "PostgreSQL", + "owner": "admin", + "shareable": true, + "parameters": { + "SSL_OPTIONS": "", + "PORT": "5434", + "CLIENT_PRIV_KEY": "", + "SSLMODE": "prefer", + "HOST": "somehost", + "CA_CERTIFICATE": "", + "DATASET": "testsuitegis", + "USER_NAME": "testsuite", + "CLIENT_CERTIFICATE": "" + } + }` + + customHttpServerHandler := func(w http.ResponseWriter, r *http.Request) { + + if r.Method == "GET" { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(responseGet)) + require.NoError(t, err) + + } + if r.Method == "PUT" { + w.WriteHeader(http.StatusNoContent) + _, err := w.Write([]byte("")) + require.NoError(t, err) + + } + + } + + parameterDoesNotExistBody := `{ + "message": "Unauthorized request by user admin due to lack of proper permissions or the object does not exist." + }` + cases := []testCase{ + { + name: "unknown flag", + statusCode: http.StatusOK, + args: []string{"connections", "update", "--name", "PostGIS 3.3 Testsuite", "--badflag"}, + wantErrOutputRegex: "unknown flag: --badflag", + }, + { + name: "500 bad status code", + statusCode: http.StatusInternalServerError, + wantErrText: "500 Internal Server Error", + args: []string{"connections", "update", "--name", "PostGIS 3.3 Testsuite"}, + }, + { + name: "missing name flag", + wantErrText: "required flag(s) \"name\" not set", + args: []string{"connections", "update"}, + }, + { + name: "delete parameter", + statusCode: http.StatusNoContent, + args: []string{"connections", "update", "--name", "PostGIS 3.3 Testsuite"}, + wantOutputRegex: "^Connection successfully updated.[\\s]*$", + httpServer: httptest.NewServer(http.HandlerFunc(customHttpServerHandler)), + }, + { + name: "parameter does not exist", + statusCode: http.StatusConflict, + body: parameterDoesNotExistBody, + args: []string{"connections", "update", "--name", "PostGIS 3.3 Testsuite"}, + wantErrText: "Unauthorized request by user admin due to lack of proper permissions or the object does not exist.", + }, + } + + runTests(cases, t) + +} diff --git a/cmd/requestfile_test.go b/cmd/requestfile_test.go index c207bf1..7e8201d 100644 --- a/cmd/requestfile_test.go +++ b/cmd/requestfile_test.go @@ -17,7 +17,7 @@ func TestLicenseRequestFile(t *testing.T) { "fmeBuild": "FME Server 2023.0 - Build 23166 - linux-x64", "code": "fc1e6bdd-3ccd-4749-a9aa-7f4ef9039c06", "serialNumber": "AAAA-AAAA-AAAA", - "fmeServerUri": "https://bobbie.base.safe.com/fmerest/v3/", + "fmeServerUri": "https://somehost/fmerest/v3/", "industry": "Industry", "publicIp": "127.0.0.1", "subscribeToUpdates": false, From 68cfea41892929c8964b087e7cd87db4271a2d7b Mon Sep 17 00:00:00 2001 From: Grant Arnold Date: Mon, 8 Apr 2024 13:52:46 -0700 Subject: [PATCH 4/7] Update docs for connections. --- docs/fmeflow.md | 1 + docs/fmeflow_connections.md | 49 ++++++++++++++++++++++++++++++ docs/fmeflow_connections_create.md | 46 ++++++++++++++++++++++++++++ docs/fmeflow_connections_delete.md | 41 +++++++++++++++++++++++++ docs/fmeflow_connections_update.md | 44 +++++++++++++++++++++++++++ docs/fmeflow_projects.md | 2 +- docs/fmeflow_projects_upload.md | 44 ++++++++++++++++++++------- 7 files changed, 215 insertions(+), 12 deletions(-) create mode 100644 docs/fmeflow_connections.md create mode 100644 docs/fmeflow_connections_create.md create mode 100644 docs/fmeflow_connections_delete.md create mode 100644 docs/fmeflow_connections_update.md diff --git a/docs/fmeflow.md b/docs/fmeflow.md index d101f99..3420944 100644 --- a/docs/fmeflow.md +++ b/docs/fmeflow.md @@ -19,6 +19,7 @@ A command line interface for interacting with FME Server. See available commands * [fmeflow backup](fmeflow_backup.md) - Backs up the FME Server configuration * [fmeflow cancel](fmeflow_cancel.md) - Cancel a running job on FME Server * [fmeflow completion](fmeflow_completion.md) - Generate the autocompletion script for the specified shell +* [fmeflow connections](fmeflow_connections.md) - Lists connections on FME Flow * [fmeflow deploymentparameters](fmeflow_deploymentparameters.md) - List Deployment Parameters * [fmeflow engines](fmeflow_engines.md) - Get information about the FME Engines * [fmeflow healthcheck](fmeflow_healthcheck.md) - Retrieves the health status of FME Server diff --git a/docs/fmeflow_connections.md b/docs/fmeflow_connections.md new file mode 100644 index 0000000..7cc5803 --- /dev/null +++ b/docs/fmeflow_connections.md @@ -0,0 +1,49 @@ +## fmeflow connections + +Lists connections on FME Flow + +### Synopsis + +Lists connections on FME Flow. Pass in a name to retrieve information on a single project. + +``` +fmeflow connections [flags] +``` + +### Examples + +``` + + # List all projects + fmeflow projects + + # List all projects owned by the user admin + fmeflow projects --owner admin +``` + +### Options + +``` + --category stringArray The categories of connections to return. Can be passed in multiple times + --excluded-type stringArray The types of connections to exclude. Can be passed in multiple times + -h, --help help for connections + --name string Return a single project with the given name. + --no-headers Don't print column headers + -o, --output string Specify the output type. Should be one of table, json, or custom-columns (default "table") + --type stringArray The types of connections to return. Can be passed in multiple times +``` + +### Options inherited from parent commands + +``` + --config string config file (default is $HOME/.config/.fmeflow-cli.yaml) + --json Output JSON +``` + +### SEE ALSO + +* [fmeflow](fmeflow.md) - A command line interface for interacting with FME Server. +* [fmeflow connections create](fmeflow_connections_create.md) - Create a connection +* [fmeflow connections delete](fmeflow_connections_delete.md) - Delete a connection +* [fmeflow connections update](fmeflow_connections_update.md) - Update a connection + diff --git a/docs/fmeflow_connections_create.md b/docs/fmeflow_connections_create.md new file mode 100644 index 0000000..dd6e130 --- /dev/null +++ b/docs/fmeflow_connections_create.md @@ -0,0 +1,46 @@ +## fmeflow connections create + +Create a connection + +### Synopsis + +Create a connection. + +``` +fmeflow connections create [flags] +``` + +### Examples + +``` + + Examples: + # Create a connection with the name "myConnection" and the category "PostgreSQL" and the type "database" with username "myUser" and password "myPassword" + fmeflow connections create --name myConnection --category database --type PostgreSQL --username myUser --password myPassword + +``` + +### Options + +``` + --authenticationMethod string Authentication method of the connection to create. + --category string Category of the connection to create. Typically it is one of: "basic", "database", "token", "oauthV1", "oauthV2". + -h, --help help for create + --name string Name of the connection to create. + --parameter stringArray Parameters of the connection to create. Must be of the form name=value. Can be specified multiple times. + --password string Password of the connection to create. + --type string Type of connection. + --username string Username of the connection to create. +``` + +### Options inherited from parent commands + +``` + --config string config file (default is $HOME/.config/.fmeflow-cli.yaml) + --json Output JSON +``` + +### SEE ALSO + +* [fmeflow connections](fmeflow_connections.md) - Lists connections on FME Flow + diff --git a/docs/fmeflow_connections_delete.md b/docs/fmeflow_connections_delete.md new file mode 100644 index 0000000..2dd719c --- /dev/null +++ b/docs/fmeflow_connections_delete.md @@ -0,0 +1,41 @@ +## fmeflow connections delete + +Delete a connection + +### Synopsis + +Delete a connection. + +``` +fmeflow connections delete [flags] +``` + +### Examples + +``` + + Examples: + # Delete a connection with the name "myConnection" + fmeflow connections delete --name myConnection + +``` + +### Options + +``` + -h, --help help for delete + --name string Name of the connection to delete. + -y, --no-prompt Description of the new repository. +``` + +### Options inherited from parent commands + +``` + --config string config file (default is $HOME/.config/.fmeflow-cli.yaml) + --json Output JSON +``` + +### SEE ALSO + +* [fmeflow connections](fmeflow_connections.md) - Lists connections on FME Flow + diff --git a/docs/fmeflow_connections_update.md b/docs/fmeflow_connections_update.md new file mode 100644 index 0000000..e580cbf --- /dev/null +++ b/docs/fmeflow_connections_update.md @@ -0,0 +1,44 @@ +## fmeflow connections update + +Update a connection + +### Synopsis + +Update a connection. + +``` +fmeflow connections update [flags] +``` + +### Examples + +``` + + Examples: + # Update a connection with the name "myConnection" and the category "PostgreSQL" and the type "database" with username "myUser" and password "myPassword" + fmeflow connections update --name myConnection --category database --username myUser --password myPassword + +``` + +### Options + +``` + --authenticationMethod string Authentication method of the connection to update. + -h, --help help for update + --name string Name of the connection to update. + --parameter stringArray Parameters of the connection to update. Must be of the form name=value. Can be specified multiple times. + --password string Password of the connection to update. + --username string Username of the connection to update. +``` + +### Options inherited from parent commands + +``` + --config string config file (default is $HOME/.config/.fmeflow-cli.yaml) + --json Output JSON +``` + +### SEE ALSO + +* [fmeflow connections](fmeflow_connections.md) - Lists connections on FME Flow + diff --git a/docs/fmeflow_projects.md b/docs/fmeflow_projects.md index 0c103c2..94c987a 100644 --- a/docs/fmeflow_projects.md +++ b/docs/fmeflow_projects.md @@ -42,5 +42,5 @@ fmeflow projects [flags] * [fmeflow](fmeflow.md) - A command line interface for interacting with FME Server. * [fmeflow projects download](fmeflow_projects_download.md) - Downloads an FME Server Project -* [fmeflow projects upload](fmeflow_projects_upload.md) - Imports FME Server Projects from a downloaded package. +* [fmeflow projects upload](fmeflow_projects_upload.md) - Imports FME Flow Projects from a downloaded package. diff --git a/docs/fmeflow_projects_upload.md b/docs/fmeflow_projects_upload.md index 2b0a28c..c80ca47 100644 --- a/docs/fmeflow_projects_upload.md +++ b/docs/fmeflow_projects_upload.md @@ -1,10 +1,14 @@ ## fmeflow projects upload -Imports FME Server Projects from a downloaded package. +Imports FME Flow Projects from a downloaded package. ### Synopsis -Imports FME Server Projects from a downloaded package. Useful for moving a project from one FME Server to another. +Imports FME Flow Projects from a downloaded package. The upload happens in two steps. The package is uploaded to the server, a preview is generated that contains the list of items, and then the import is run. This command can be run using a few different modes. +- Using the --get-selectable flag will just generate the preview and output the selectable items in the package and then delete the import +- Using the --quick flag will skip the preview and import everything in the package by default. +- Using the --interactive flag will prompt the user to select items to import from the list of selectable items if any exist +- Using the --selected-items flag will import only the items specified. The default is to import all items in the package. ``` fmeflow projects upload [flags] @@ -14,22 +18,40 @@ fmeflow projects upload [flags] ``` - # Restore from a backup in a local file + # Upload a project and import all selectable items if any exist fmeflow projects upload --file ProjectPackage.fsproject - # Restore from a backup in a local file using UPDATE mode - fmeflow projects upload --file ProjectPackage.fsproject --import-mode UPDATE + # Upload a project without overwriting existing items + fmeflow projects upload --file ProjectPackage.fsproject --overwrite=false + + # Upload a project and perform a quick import + fmeflow projects upload --file ProjectPackage.fsproject --quick + + # Upload a project and be prompted for which items to import of the selectable items + fmeflow projects upload --file ProjectPackage.fsproject --interactive + + # Upload a project and get the list of selectable items + fmeflow projects upload --file ProjectPackage.fsproject --get-selectable + + # Upload a project and import only the specified selectable items + fme projects upload --file ProjectPackage.fsproject --selected-items="mysqldb:connection,slack con:connector" ``` ### Options ``` - --disable-project-items Whether to disable items in the imported FME Server Projects. If true, items that are new or overwritten will be imported but disabled. If false, project items are imported as defined in the import package. - -f, --file string Path to backup file to upload to restore. Can be a local file or the relative path inside the specified shared resource. - -h, --help help for upload - --import-mode string To import only items in the import package that do not exist on the current instance, specify INSERT. To overwrite items on the current instance with those in the import package, specify UPDATE. Default is INSERT. (default "INSERT") - --pause-notifications Disable notifications for the duration of the restore. (default true) - --projects-import-mode string Import mode for projects. To import only projects in the import package that do not exist on the current instance, specify INSERT. To overwrite projects on the current instance with those in the import package, specify UPDATE. If not supplied, importMode will be used. + --disable-project-items Whether to disable items in the imported FME Server Projects. If true, items that are new or overwritten will be imported but disabled. If false, project items are imported as defined in the import package. + --failure-topic string Topic to notify on failure of the backup. (default "MIGRATION_ASYNC_JOB_FAILURE") + -f, --file string Path to backup file to upload to restore. Can be a local file or the relative path inside the specified shared resource. + --get-selectable Output the selectable items in the import package. + -h, --help help for upload + --interactive Prompt interactively for the selectable items to import (if any exist). + --overwrite If specified, the items in the project will overwrite existing items. (default true) + --pause-notifications Disable notifications for the duration of the restore. (default true) + --quick Import everything in the package by default. + --selected-items string The items to import. Set to "all" to import all items, and "none" to omit selectable items. Otherwise, this should be a comma separated list of item ids type pairs separated by a colon. e.g. a:b,c:d (default "all") + --success-topic string Topic to notify on success of the backup. (default "MIGRATION_ASYNC_JOB_SUCCESS") + --wait Wait for import to complete. Set to false to return immediately after the import is started. (default true) ``` ### Options inherited from parent commands From 6fc9124bf886ca1a6866fb1a34675bec622ce693 Mon Sep 17 00:00:00 2001 From: Grant Arnold Date: Fri, 12 Apr 2024 15:20:21 -0700 Subject: [PATCH 5/7] Refresh docs. --- docs/fmeflow.md | 7 +++++++ docs/fmeflow_deploymentparameters_create.md | 10 +++++++--- docs/fmeflow_deploymentparameters_update.md | 10 +++++++--- docs/fmeflow_projects_download.md | 12 ++++++------ 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/docs/fmeflow.md b/docs/fmeflow.md index 3420944..96079dc 100644 --- a/docs/fmeflow.md +++ b/docs/fmeflow.md @@ -6,6 +6,13 @@ A command line interface for interacting with FME Server. A command line interface for interacting with FME Server. See available commands below. Get started with the login command. +### Examples + +``` +# Get started with the login command +fmeserver login https://my-fmeserver.internal +``` + ### Options ``` diff --git a/docs/fmeflow_deploymentparameters_create.md b/docs/fmeflow_deploymentparameters_create.md index fb5aca6..73793c8 100644 --- a/docs/fmeflow_deploymentparameters_create.md +++ b/docs/fmeflow_deploymentparameters_create.md @@ -23,9 +23,13 @@ fmeflow deploymentparameters create [flags] ### Options ``` - -h, --help help for create - --name string Name of the deployment parameter to create. - --value string The value to set the deployment parameter to. + --database-type string The type of the database to use for the database deployment parameter. (Optional) + --excluded-service stringArray Service to exclude in the deployment parameter. Can be passed in multiple times if there are multiple Web services to exclude. + -h, --help help for create + --included-service stringArray Service to include in the deployment parameter. Can be passed in multiple times if there are multiple Web services to include. + --name string Name of the deployment parameter to create. + --type string Type of parameter to create. Must be one of text, database, or web. Default is text. + --value string The value to set the deployment parameter to. (Optional) ``` ### Options inherited from parent commands diff --git a/docs/fmeflow_deploymentparameters_update.md b/docs/fmeflow_deploymentparameters_update.md index 2e43201..618a00f 100644 --- a/docs/fmeflow_deploymentparameters_update.md +++ b/docs/fmeflow_deploymentparameters_update.md @@ -23,9 +23,13 @@ fmeflow deploymentparameters update [flags] ### Options ``` - -h, --help help for update - --name string Name of the deployment parameter to update. - --value string The value to set the deployment parameter to. + --database-type string The type of the database to use for the database deployment parameter. (Optional) + --excluded-service stringArray Service to exclude in the deployment parameter. Can be passed in multiple times if there are multiple Web services to exclude. + -h, --help help for update + --included-service stringArray Service to include in the deployment parameter. Can be passed in multiple times if there are multiple Web services to include. + --name string Name of the deployment parameter to update. + --type string Update the type of the parameter. Must be one of text, database, or web. Default is text. + --value string The value to set the deployment parameter to. ``` ### Options inherited from parent commands diff --git a/docs/fmeflow_projects_download.md b/docs/fmeflow_projects_download.md index adf8044..3f42cf8 100644 --- a/docs/fmeflow_projects_download.md +++ b/docs/fmeflow_projects_download.md @@ -24,12 +24,12 @@ fmeflow projects download [flags] ### Options ``` - --exclude-selectable-items Excludes all selectable item in this package. Default is false. - --exclude-sensitive-info Whether to exclude sensitive information from the exported package. Sensitive information will be excluded from connections, subscriptions, publications, schedule tasks, S3 resources, and user accounts. Other items in the project may still contain sensitive data, especially workspaces. Please be careful before sharing the project export pacakge with others. - -f, --file string Path to file to download the backup to. (default "ProjectPackage.fsproject") - -h, --help help for download - --id string ID of the project to download. - --name string Name of the project to download. + --exclude-all-selectable-items Excludes all selectable item in this package. Default is false. + --exclude-sensitive-info Whether to exclude sensitive information from the exported package. Sensitive information will be excluded from connections, subscriptions, publications, schedule tasks, S3 resources, and user accounts. Other items in the project may still contain sensitive data, especially workspaces. Please be careful before sharing the project export pacakge with others. + -f, --file string Path to file to download the backup to. (default "ProjectPackage.fsproject") + -h, --help help for download + --id string ID of the project to download. + --name string Name of the project to download. ``` ### Options inherited from parent commands From eac91ff4aa02d569ba583a99a2e6e5ff55f2af21 Mon Sep 17 00:00:00 2001 From: Grant Arnold Date: Mon, 15 Apr 2024 18:09:24 +0000 Subject: [PATCH 6/7] Fix up examples for connections. Clean example text. Regen doc. --- cmd/connections.go | 21 ++++++++---- cmd/connections_create.go | 10 +++--- cmd/connections_create_test.go | 4 +-- cmd/connections_delete.go | 5 ++- cmd/connections_update.go | 9 +++-- cmd/deploymentparameters.go | 13 ++++--- cmd/deploymentparameters_create.go | 5 ++- cmd/deploymentparameters_delete.go | 9 +++-- cmd/deploymentparameters_update.go | 6 ++-- cmd/repositories.go | 21 ++++++------ cmd/repositories_create.go | 9 +++-- cmd/repositories_delete.go | 9 +++-- cmd/run.go | 29 ++++++++-------- cmd/workspaces.go | 25 +++++++------- docs/custom-columns.md | 2 +- docs/fmeflow_connections.md | 19 ++++++++--- docs/fmeflow_connections_create.md | 24 +++++++------ docs/fmeflow_connections_delete.md | 5 ++- docs/fmeflow_connections_update.md | 9 +++-- docs/fmeflow_deploymentparameters.md | 13 ++++--- docs/fmeflow_deploymentparameters_create.md | 5 ++- docs/fmeflow_deploymentparameters_delete.md | 9 +++-- docs/fmeflow_deploymentparameters_update.md | 6 ++-- docs/fmeflow_repositories.md | 21 ++++++------ docs/fmeflow_repositories_create.md | 9 +++-- docs/fmeflow_repositories_delete.md | 9 +++-- docs/fmeflow_run.md | 38 ++++++++++++--------- docs/fmeflow_workspaces.md | 25 +++++++------- 28 files changed, 188 insertions(+), 181 deletions(-) diff --git a/cmd/connections.go b/cmd/connections.go index acaaf68..c205781 100644 --- a/cmd/connections.go +++ b/cmd/connections.go @@ -42,13 +42,22 @@ func newConnectionsCmd() *cobra.Command { cmd := &cobra.Command{ Use: "connections", Short: "Lists connections on FME Flow", - Long: "Lists connections on FME Flow. Pass in a name to retrieve information on a single project.", + Long: "Lists connections on FME Flow. Pass in a name to retrieve information on a single connection.", Example: ` - # List all projects - fmeflow projects - - # List all projects owned by the user admin - fmeflow projects --owner admin`, + # List all connections + fmeflow connections + + # Get a single connection with the name "myConnection" + fmeflow connections --name myConnection + + # List all connections of type "Google Drive" + fmeflow connections --type "Google Drive" + + # List all database connections + fmeflow connections --category database + + # List the PostgreSQL connections with custom columns showing the name and host of the database connections + fmeflow connections --category "database" --type "PostgreSQL" --output=custom-columns="NAME:.name,HOST:.parameters.HOST" `, Args: NoArgs, RunE: connectionsRun(&f), } diff --git a/cmd/connections_create.go b/cmd/connections_create.go index a87a785..21e0a94 100644 --- a/cmd/connections_create.go +++ b/cmd/connections_create.go @@ -44,9 +44,11 @@ func newConnectionCreateCmd() *cobra.Command { Short: "Create a connection", Long: `Create a connection.`, Example: ` - Examples: - # Create a connection with the name "myConnection" and the category "PostgreSQL" and the type "database" with username "myUser" and password "myPassword" - fmeflow connections create --name myConnection --category database --type PostgreSQL --username myUser --password myPassword + # Create a PostgreSQL connection + fmeflow connections create --name myPGSQLConnection --category database --type PostgreSQL --parameter HOST=myDBHost --parameter PORT=5432 --parameter DATASET=dbname --parameter USER_NAME=dbuser --parameter SSL_OPTIONS="" --parameter SSLMODE=prefer + + # Create a Google Drive connection (web service must already exist on FME Flow) + fmeflow connections create --name googleDriveConn --category oauthV2 --type "Google Drive" `, Args: NoArgs, @@ -56,7 +58,7 @@ func newConnectionCreateCmd() *cobra.Command { cmd.Flags().StringVar(&f.name, "name", "", "Name of the connection to create.") cmd.Flags().StringVar(&f.category, "category", "", "Category of the connection to create. Typically it is one of: \"basic\", \"database\", \"token\", \"oauthV1\", \"oauthV2\".") cmd.Flags().StringVar(&f.connectionType, "type", "", "Type of connection.") - cmd.Flags().StringVar(&f.authenticationMethod, "authenticationMethod", "", "Authentication method of the connection to create.") + cmd.Flags().StringVar(&f.authenticationMethod, "authentication-method", "", "Authentication method of the connection to create.") cmd.Flags().StringVar(&f.username, "username", "", "Username of the connection to create.") cmd.Flags().StringVar(&f.password, "password", "", "Password of the connection to create.") cmd.Flags().StringArrayVar(&f.parameter, "parameter", []string{}, "Parameters of the connection to create. Must be of the form name=value. Can be specified multiple times.") diff --git a/cmd/connections_create_test.go b/cmd/connections_create_test.go index 4f550b2..58ee43d 100644 --- a/cmd/connections_create_test.go +++ b/cmd/connections_create_test.go @@ -38,13 +38,13 @@ func TestConnectionsCreate(t *testing.T) { { name: "create connection", statusCode: http.StatusCreated, - args: []string{"connections", "create", "--name", "test123aa", "--category", "database", "--type", "PostgreSQL", "--username", "test", "--password", "test", "--parameter", "HOST=a", "--parameter", "PORT=5432", "--parameter", "DATASET=dbname", "--parameter", "USER_NAME=a", "--parameter", "SSL_OPTIONS=a", "--parameter", "SSLMODE=prefer"}, + args: []string{"connections", "create", "--name", "test123aa", "--category", "database", "--type", "PostgreSQL", "--parameter", "HOST=a", "--parameter", "PORT=5432", "--parameter", "DATASET=dbname", "--parameter", "USER_NAME=a", "--parameter", "SSL_OPTIONS=a", "--parameter", "SSLMODE=prefer"}, wantOutputRegex: "^[\\s]*Connection successfully created.[\\s]*$", }, { name: "create connection json output", statusCode: http.StatusCreated, - args: []string{"connections", "create", "--name", "test123aa", "--category", "database", "--type", "PostgreSQL", "--username", "test", "--password", "test", "--parameter", "HOST=a", "--parameter", "PORT=5432", "--parameter", "DATASET=dbname", "--parameter", "USER_NAME=a", "--parameter", "SSL_OPTIONS=a", "--parameter", "SSLMODE=prefer", "--json"}, + args: []string{"connections", "create", "--name", "test123aa", "--category", "database", "--type", "PostgreSQL", "--parameter", "HOST=a", "--parameter", "PORT=5432", "--parameter", "DATASET=dbname", "--parameter", "USER_NAME=a", "--parameter", "SSL_OPTIONS=a", "--parameter", "SSLMODE=prefer", "--json"}, wantOutputJson: "{}", }, { diff --git a/cmd/connections_delete.go b/cmd/connections_delete.go index abf4603..86b6846 100644 --- a/cmd/connections_delete.go +++ b/cmd/connections_delete.go @@ -23,9 +23,8 @@ func newConnectionDeleteCmd() *cobra.Command { Short: "Delete a connection", Long: `Delete a connection.`, Example: ` - Examples: - # Delete a connection with the name "myConnection" - fmeflow connections delete --name myConnection + # Delete a connection with the name "myConnection" + fmeflow connections delete --name myConnection `, Args: NoArgs, diff --git a/cmd/connections_update.go b/cmd/connections_update.go index 497b0da..e9f1666 100644 --- a/cmd/connections_update.go +++ b/cmd/connections_update.go @@ -33,11 +33,10 @@ func newConnectionUpdateCmd() *cobra.Command { cmd := &cobra.Command{ Use: "update", Short: "Update a connection", - Long: `Update a connection.`, + Long: `Update a connection. Only things that need to be modified need to be specified.`, Example: ` - Examples: - # Update a connection with the name "myConnection" and the category "PostgreSQL" and the type "database" with username "myUser" and password "myPassword" - fmeflow connections update --name myConnection --category database --username myUser --password myPassword + # Update a PostgreSQL connection with the name "myPGSQLConnection" and modify the host to "myDBHost" + fmeflow connections update --name myPGSQLConnection --parameter HOST=myDBHost `, Args: NoArgs, @@ -45,7 +44,7 @@ func newConnectionUpdateCmd() *cobra.Command { } cmd.Flags().StringVar(&f.name, "name", "", "Name of the connection to update.") - cmd.Flags().StringVar(&f.authenticationMethod, "authenticationMethod", "", "Authentication method of the connection to update.") + cmd.Flags().StringVar(&f.authenticationMethod, "authentication-method", "", "Authentication method of the connection to update.") cmd.Flags().StringVar(&f.username, "username", "", "Username of the connection to update.") cmd.Flags().StringVar(&f.password, "password", "", "Password of the connection to update.") cmd.Flags().StringArrayVar(&f.parameter, "parameter", []string{}, "Parameters of the connection to update. Must be of the form name=value. Can be specified multiple times.") diff --git a/cmd/deploymentparameters.go b/cmd/deploymentparameters.go index 44df4fa..3569cb1 100644 --- a/cmd/deploymentparameters.go +++ b/cmd/deploymentparameters.go @@ -50,15 +50,14 @@ func newDeploymentParametersCmd() *cobra.Command { Short: "List Deployment Parameters", Long: `Lists Deployment Parameters on the given FME Server.`, Example: ` - Examples: - # List all deployment parameters - fmeflow deploymentparameters + # List all deployment parameters + fmeflow deploymentparameters - # List a single deployment parameter - fmeflow deploymentparameters --name testParameter + # List a single deployment parameter + fmeflow deploymentparameters --name testParameter - # Output all deploymentparameters in json format - fmeflow deploymentparameters --json`, + # Output all deploymentparameters in json format + fmeflow deploymentparameters --json`, Args: NoArgs, RunE: deploymentParametersRun(&f), } diff --git a/cmd/deploymentparameters_create.go b/cmd/deploymentparameters_create.go index 5382b28..9bcfdb2 100644 --- a/cmd/deploymentparameters_create.go +++ b/cmd/deploymentparameters_create.go @@ -39,9 +39,8 @@ func newDeploymentParameterCreateCmd() *cobra.Command { Short: "Create a deployment parameter", Long: `Create a deployment parameter.`, Example: ` - Examples: - # Create a deployment parameter with the name "myParam" and the value "myValue" - fmeflow deploymentparameters create --name myParam --value myValue + # Create a deployment parameter with the name "myParam" and the value "myValue" + fmeflow deploymentparameters create --name myParam --value myValue `, Args: NoArgs, diff --git a/cmd/deploymentparameters_delete.go b/cmd/deploymentparameters_delete.go index 26e1c18..5e85b6d 100644 --- a/cmd/deploymentparameters_delete.go +++ b/cmd/deploymentparameters_delete.go @@ -23,12 +23,11 @@ func newDeploymentParameterDeleteCmd() *cobra.Command { Short: "Delete a deployment parameter", Long: `Delete a deployment parameter.`, Example: ` - Examples: - # Delete adeployment parameter with the name "myParam" - fmeflow deploymentparameters delete --name myParam + # Delete adeployment parameter with the name "myParam" + fmeflow deploymentparameters delete --name myParam - # Delete a repository with the name "myRepository" and no confirmation - fmeflow deploymentparameters delete --name myParam --no-prompt + # Delete a repository with the name "myRepository" and no confirmation + fmeflow deploymentparameters delete --name myParam --no-prompt `, Args: NoArgs, RunE: deploymentParameterDeleteRun(&f), diff --git a/cmd/deploymentparameters_update.go b/cmd/deploymentparameters_update.go index 2e8f6a2..3928e37 100644 --- a/cmd/deploymentparameters_update.go +++ b/cmd/deploymentparameters_update.go @@ -40,9 +40,9 @@ func newDeploymentParameterUpdateCmd() *cobra.Command { Short: "Update a deployment parameter", Long: `Update a deployment parameter.`, Example: ` - Examples: - # Update a deployment parameter with the name "myParam" and the value "myValue" - fmeflow deploymentparameters update --name myParam --value myValue + + # Update a deployment parameter with the name "myParam" and the value "myValue" + fmeflow deploymentparameters update --name myParam --value myValue `, Args: NoArgs, diff --git a/cmd/repositories.go b/cmd/repositories.go index 92e1788..dfabef6 100644 --- a/cmd/repositories.go +++ b/cmd/repositories.go @@ -66,21 +66,20 @@ func newRepositoryCmd() *cobra.Command { Short: "List repositories", Long: `Lists repositories on the given FME Server. Pass in a name to get information on a specific repository.`, Example: ` - Examples: - # List all repositories - fmeflow repositories + # List all repositories + fmeflow repositories - # List all repositories owned by the admin user - fmeflow repositories --owner admin + # List all repositories owned by the admin user + fmeflow repositories --owner admin - # List a single repository with the name "Samples" - fmeflow repositories --name Samples + # List a single repository with the name "Samples" + fmeflow repositories --name Samples - # Output just the name of all the repositories - fmeflow repositories --output=custom-columns=NAME:.name --no-headers + # Output just the name of all the repositories + fmeflow repositories --output=custom-columns=NAME:.name --no-headers - # Output all repositories in json format - fmeflow repositories --json`, + # Output all repositories in json format + fmeflow repositories --json`, Args: NoArgs, PreRunE: func(cmd *cobra.Command, args []string) error { // get build to decide if we should use v3 or v4 diff --git a/cmd/repositories_create.go b/cmd/repositories_create.go index 23b3be4..78b8e2e 100644 --- a/cmd/repositories_create.go +++ b/cmd/repositories_create.go @@ -32,12 +32,11 @@ func newRepositoryCreateCmd() *cobra.Command { Short: "Create a new repository.", Long: `Create a new repository.`, Example: ` - Examples: - # Create a repository with the name "myRepository" and no description - fmeflow repositories create --name myRepository + # Create a repository with the name "myRepository" and no description + fmeflow repositories create --name myRepository - # Output just the name of all the repositories - fmeflow repositories create --name myRepository --description "This is my new repository" + # Output just the name of all the repositories + fmeflow repositories create --name myRepository --description "This is my new repository" `, PreRunE: func(cmd *cobra.Command, args []string) error { // get build to decide if we should use v3 or v4 diff --git a/cmd/repositories_delete.go b/cmd/repositories_delete.go index 8098cfc..c8c1300 100644 --- a/cmd/repositories_delete.go +++ b/cmd/repositories_delete.go @@ -25,12 +25,11 @@ func newRepositoryDeleteCmd() *cobra.Command { Short: "Delete a repository.", Long: `Delete a repository.`, Example: ` - Examples: - # Delete a repository with the name "myRepository" - fmeflow repositories delete --name myRepository + # Delete a repository with the name "myRepository" + fmeflow repositories delete --name myRepository - # Delete a repository with the name "myRepository" and no confirmation - fmeflow repositories delete --name myRepository --no-prompt + # Delete a repository with the name "myRepository" and no confirmation + fmeflow repositories delete --name myRepository --no-prompt `, PreRunE: func(cmd *cobra.Command, args []string) error { // get build to decide if we should use v3 or v4 diff --git a/cmd/run.go b/cmd/run.go index ec57fa3..69757c4 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -95,26 +95,25 @@ func newRunCmd() *cobra.Command { cmd := &cobra.Command{ Use: "run", Short: "Run a workspace on FME Server.", - Long: `Run a workspace on FME Server. - - Examples: - # Submit a job asynchronously - fmeflow run --repository Samples --workspace austinApartments.fmw + Long: `Run a workspace on FME Server.`, + Example: ` + # Submit a job asynchronously + fmeflow run --repository Samples --workspace austinApartments.fmw - # Submit a job and wait for it to complete - fmeflow run --repository Samples --workspace austinApartments.fmw --wait + # Submit a job and wait for it to complete + fmeflow run --repository Samples --workspace austinApartments.fmw --wait - # Submit a job to a specific queue and set a time to live in the queue - fmeflow run --repository Samples --workspace austinApartments.fmw --tag Queue1 --time-to-live 120 + # Submit a job to a specific queue and set a time to live in the queue + fmeflow run --repository Samples --workspace austinApartments.fmw --tag Queue1 --time-to-live 120 - # Submit a job and pass in a few published parameters - fmeflow run --repository Samples --workspace austinDownload.fmw --published-parameter-list THEMES=railroad,airports --published-parameter COORDSYS=TX83-CF + # Submit a job and pass in a few published parameters + fmeflow run --repository Samples --workspace austinDownload.fmw --published-parameter-list THEMES=railroad,airports --published-parameter COORDSYS=TX83-CF - # Submit a job, wait for it to complete, and customize the output - fmeflow run --repository Samples --workspace austinApartments.fmw --wait --output="custom-columns=Time Requested:.timeRequested,Time Started:.timeStarted,Time Finished:.timeFinished" + # Submit a job, wait for it to complete, and customize the output + fmeflow run --repository Samples --workspace austinApartments.fmw --wait --output="custom-columns=Time Requested:.timeRequested,Time Started:.timeStarted,Time Finished:.timeFinished" - # Upload a local file to use as the source data for the translation - fmeflow run --repository Samples --workspace austinApartments.fmw --file Landmarks-edited.sqlite --wait`, + # Upload a local file to use as the source data for the translation + fmeflow run --repository Samples --workspace austinApartments.fmw --file Landmarks-edited.sqlite --wait`, Args: NoArgs, RunE: runRun(&f), } diff --git a/cmd/workspaces.go b/cmd/workspaces.go index 8c424d5..bb9ee1d 100644 --- a/cmd/workspaces.go +++ b/cmd/workspaces.go @@ -338,24 +338,23 @@ func newWorkspaceCmd() *cobra.Command { Short: "List workspaces.", Long: `Lists workspaces that exist on the FME Server. Filter by repository, specify a name to retrieve a specific workspace, or specify a filter string to narrow down by name or title.`, Example: ` - Examples: - # List all workspaces on the FME Server - fmeflow workspaces + # List all workspaces on the FME Server + fmeflow workspaces - # List all workspaces in Samples repository - fmeflow workspaces --repository Samples + # List all workspaces in Samples repository + fmeflow workspaces --repository Samples - # List all workspaces in the Samples repository and output it in json - fmeflow workspaces --repository Samples --json + # List all workspaces in the Samples repository and output it in json + fmeflow workspaces --repository Samples --json - # List all workspaces in the Samples repository with custom columns showing the last publish date and number of times run - fmeflow workspaces --repository Samples --output="custom-columns=NAME:.name,PUBLISH DATE:.lastPublishDate,TOTAL RUNS:.totalRuns" + # List all workspaces in the Samples repository with custom columns showing the last publish date and number of times run + fmeflow workspaces --repository Samples --output="custom-columns=NAME:.name,PUBLISH DATE:.lastPublishDate,TOTAL RUNS:.totalRuns" - # Get information on a single workspace - fmeflow workspaces --repository Samples --name austinApartments.fmw + # Get information on a single workspace + fmeflow workspaces --repository Samples --name austinApartments.fmw - # Get the name, source format, and destination format for this workspace - fmeflow workspaces --repository Samples --name austinApartments.fmw --output=custom-columns=NAME:.name,SOURCE:.datasets.source[*].format,DEST:.datasets.destination[*].format`, + # Get the name, source format, and destination format for this workspace + fmeflow workspaces --repository Samples --name austinApartments.fmw --output=custom-columns=NAME:.name,SOURCE:.datasets.source[*].format,DEST:.datasets.destination[*].format`, Args: NoArgs, PreRunE: func(cmd *cobra.Command, args []string) error { // get build to decide if we should use v3 or v4 diff --git a/docs/custom-columns.md b/docs/custom-columns.md index 4c20580..86d9222 100644 --- a/docs/custom-columns.md +++ b/docs/custom-columns.md @@ -190,7 +190,7 @@ For a slightly more complicated example, specifying a specific workspace gives m } ``` -The JSON about a single workspace includes information on source and destination datasets, that are stored in a JSON list. In JSONPath, lists are accessed using `[]`, with a specific index being specified as a number such as `[0]`, or all results from that list being denoted as `[*]`. For examples, if we want to get all the source formats for this workspace, we would use the JSonPath query `.datasets.source[*].format`. A full example: +The JSON about a single workspace includes information on source and destination datasets, that are stored in a JSON list. In JSONPath, lists are accessed using `[]`, with a specific index being specified as a number such as `[0]`, or all results from that list being denoted as `[*]`. For example, if we want to get all the source formats for this workspace, we would use the JSonPath query `.datasets.source[*].format`. A full example: ``` > fmeflow workspaces --repository Samples --name "austinApartments.fmw" --output custom-columns="NAME:.name,SOURCE:.datasets.source[*].format,DEST:datasets.destination[*].format" diff --git a/docs/fmeflow_connections.md b/docs/fmeflow_connections.md index 7cc5803..633ef96 100644 --- a/docs/fmeflow_connections.md +++ b/docs/fmeflow_connections.md @@ -4,7 +4,7 @@ Lists connections on FME Flow ### Synopsis -Lists connections on FME Flow. Pass in a name to retrieve information on a single project. +Lists connections on FME Flow. Pass in a name to retrieve information on a single connection. ``` fmeflow connections [flags] @@ -14,11 +14,20 @@ fmeflow connections [flags] ``` - # List all projects - fmeflow projects + # List all connections + fmeflow connections - # List all projects owned by the user admin - fmeflow projects --owner admin + # Get a single connection with the name "myConnection" + fmeflow connections --name myConnection + + # List all connections of type "Google Drive" + fmeflow connections --type "Google Drive" + + # List all database connections + fmeflow connections --category database + + # List the PostgreSQL connections with custom columns showing the name and host of the database connections + fmeflow connections --category "database" --type "PostgreSQL" --output=custom-columns="NAME:.name,HOST:.parameters.HOST" ``` ### Options diff --git a/docs/fmeflow_connections_create.md b/docs/fmeflow_connections_create.md index dd6e130..a957e3a 100644 --- a/docs/fmeflow_connections_create.md +++ b/docs/fmeflow_connections_create.md @@ -14,23 +14,25 @@ fmeflow connections create [flags] ``` - Examples: - # Create a connection with the name "myConnection" and the category "PostgreSQL" and the type "database" with username "myUser" and password "myPassword" - fmeflow connections create --name myConnection --category database --type PostgreSQL --username myUser --password myPassword + # Create a PostgreSQL connection + fmeflow connections create --name myPGSQLConnection --category database --type PostgreSQL --parameter HOST=myDBHost --parameter PORT=5432 --parameter DATASET=dbname --parameter USER_NAME=dbuser --parameter SSL_OPTIONS="" --parameter SSLMODE=prefer + + # Create a Google Drive connection (web service must already exist on FME Flow) + fmeflow connections create --name googleDriveConn --category oauthV2 --type "Google Drive" ``` ### Options ``` - --authenticationMethod string Authentication method of the connection to create. - --category string Category of the connection to create. Typically it is one of: "basic", "database", "token", "oauthV1", "oauthV2". - -h, --help help for create - --name string Name of the connection to create. - --parameter stringArray Parameters of the connection to create. Must be of the form name=value. Can be specified multiple times. - --password string Password of the connection to create. - --type string Type of connection. - --username string Username of the connection to create. + --authentication-method string Authentication method of the connection to create. + --category string Category of the connection to create. Typically it is one of: "basic", "database", "token", "oauthV1", "oauthV2". + -h, --help help for create + --name string Name of the connection to create. + --parameter stringArray Parameters of the connection to create. Must be of the form name=value. Can be specified multiple times. + --password string Password of the connection to create. + --type string Type of connection. + --username string Username of the connection to create. ``` ### Options inherited from parent commands diff --git a/docs/fmeflow_connections_delete.md b/docs/fmeflow_connections_delete.md index 2dd719c..e4ab885 100644 --- a/docs/fmeflow_connections_delete.md +++ b/docs/fmeflow_connections_delete.md @@ -14,9 +14,8 @@ fmeflow connections delete [flags] ``` - Examples: - # Delete a connection with the name "myConnection" - fmeflow connections delete --name myConnection + # Delete a connection with the name "myConnection" + fmeflow connections delete --name myConnection ``` diff --git a/docs/fmeflow_connections_update.md b/docs/fmeflow_connections_update.md index e580cbf..9d5eb96 100644 --- a/docs/fmeflow_connections_update.md +++ b/docs/fmeflow_connections_update.md @@ -4,7 +4,7 @@ Update a connection ### Synopsis -Update a connection. +Update a connection. Only things that need to be modified need to be specified. ``` fmeflow connections update [flags] @@ -14,16 +14,15 @@ fmeflow connections update [flags] ``` - Examples: - # Update a connection with the name "myConnection" and the category "PostgreSQL" and the type "database" with username "myUser" and password "myPassword" - fmeflow connections update --name myConnection --category database --username myUser --password myPassword + # Update a PostgreSQL connection with the name "myPGSQLConnection" and modify the host to "myDBHost" + fmeflow connections update --name myPGSQLConnection --parameter HOST=myDBHost ``` ### Options ``` - --authenticationMethod string Authentication method of the connection to update. + --authentication-method string Authentication method of the connection to update. -h, --help help for update --name string Name of the connection to update. --parameter stringArray Parameters of the connection to update. Must be of the form name=value. Can be specified multiple times. diff --git a/docs/fmeflow_deploymentparameters.md b/docs/fmeflow_deploymentparameters.md index f63c844..b85c9e2 100644 --- a/docs/fmeflow_deploymentparameters.md +++ b/docs/fmeflow_deploymentparameters.md @@ -14,15 +14,14 @@ fmeflow deploymentparameters [flags] ``` - Examples: - # List all deployment parameters - fmeflow deploymentparameters + # List all deployment parameters + fmeflow deploymentparameters - # List a single deployment parameter - fmeflow deploymentparameters --name testParameter + # List a single deployment parameter + fmeflow deploymentparameters --name testParameter - # Output all deploymentparameters in json format - fmeflow deploymentparameters --json + # Output all deploymentparameters in json format + fmeflow deploymentparameters --json ``` ### Options diff --git a/docs/fmeflow_deploymentparameters_create.md b/docs/fmeflow_deploymentparameters_create.md index 73793c8..aceb51f 100644 --- a/docs/fmeflow_deploymentparameters_create.md +++ b/docs/fmeflow_deploymentparameters_create.md @@ -14,9 +14,8 @@ fmeflow deploymentparameters create [flags] ``` - Examples: - # Create a deployment parameter with the name "myParam" and the value "myValue" - fmeflow deploymentparameters create --name myParam --value myValue + # Create a deployment parameter with the name "myParam" and the value "myValue" + fmeflow deploymentparameters create --name myParam --value myValue ``` diff --git a/docs/fmeflow_deploymentparameters_delete.md b/docs/fmeflow_deploymentparameters_delete.md index 2df1031..8210879 100644 --- a/docs/fmeflow_deploymentparameters_delete.md +++ b/docs/fmeflow_deploymentparameters_delete.md @@ -14,12 +14,11 @@ fmeflow deploymentparameters delete [flags] ``` - Examples: - # Delete adeployment parameter with the name "myParam" - fmeflow deploymentparameters delete --name myParam + # Delete adeployment parameter with the name "myParam" + fmeflow deploymentparameters delete --name myParam - # Delete a repository with the name "myRepository" and no confirmation - fmeflow deploymentparameters delete --name myParam --no-prompt + # Delete a repository with the name "myRepository" and no confirmation + fmeflow deploymentparameters delete --name myParam --no-prompt ``` diff --git a/docs/fmeflow_deploymentparameters_update.md b/docs/fmeflow_deploymentparameters_update.md index 618a00f..8e3e77c 100644 --- a/docs/fmeflow_deploymentparameters_update.md +++ b/docs/fmeflow_deploymentparameters_update.md @@ -14,9 +14,9 @@ fmeflow deploymentparameters update [flags] ``` - Examples: - # Update a deployment parameter with the name "myParam" and the value "myValue" - fmeflow deploymentparameters update --name myParam --value myValue + + # Update a deployment parameter with the name "myParam" and the value "myValue" + fmeflow deploymentparameters update --name myParam --value myValue ``` diff --git a/docs/fmeflow_repositories.md b/docs/fmeflow_repositories.md index 18ea32a..1b24ef8 100644 --- a/docs/fmeflow_repositories.md +++ b/docs/fmeflow_repositories.md @@ -14,21 +14,20 @@ fmeflow repositories [flags] ``` - Examples: - # List all repositories - fmeflow repositories + # List all repositories + fmeflow repositories - # List all repositories owned by the admin user - fmeflow repositories --owner admin + # List all repositories owned by the admin user + fmeflow repositories --owner admin - # List a single repository with the name "Samples" - fmeflow repositories --name Samples + # List a single repository with the name "Samples" + fmeflow repositories --name Samples - # Output just the name of all the repositories - fmeflow repositories --output=custom-columns=NAME:.name --no-headers + # Output just the name of all the repositories + fmeflow repositories --output=custom-columns=NAME:.name --no-headers - # Output all repositories in json format - fmeflow repositories --json + # Output all repositories in json format + fmeflow repositories --json ``` ### Options diff --git a/docs/fmeflow_repositories_create.md b/docs/fmeflow_repositories_create.md index fd82bee..f96e89d 100644 --- a/docs/fmeflow_repositories_create.md +++ b/docs/fmeflow_repositories_create.md @@ -14,12 +14,11 @@ fmeflow repositories create [flags] ``` - Examples: - # Create a repository with the name "myRepository" and no description - fmeflow repositories create --name myRepository + # Create a repository with the name "myRepository" and no description + fmeflow repositories create --name myRepository - # Output just the name of all the repositories - fmeflow repositories create --name myRepository --description "This is my new repository" + # Output just the name of all the repositories + fmeflow repositories create --name myRepository --description "This is my new repository" ``` diff --git a/docs/fmeflow_repositories_delete.md b/docs/fmeflow_repositories_delete.md index 0b68194..604aaf8 100644 --- a/docs/fmeflow_repositories_delete.md +++ b/docs/fmeflow_repositories_delete.md @@ -14,12 +14,11 @@ fmeflow repositories delete [flags] ``` - Examples: - # Delete a repository with the name "myRepository" - fmeflow repositories delete --name myRepository + # Delete a repository with the name "myRepository" + fmeflow repositories delete --name myRepository - # Delete a repository with the name "myRepository" and no confirmation - fmeflow repositories delete --name myRepository --no-prompt + # Delete a repository with the name "myRepository" and no confirmation + fmeflow repositories delete --name myRepository --no-prompt ``` diff --git a/docs/fmeflow_run.md b/docs/fmeflow_run.md index f4f41f3..d92d4dc 100644 --- a/docs/fmeflow_run.md +++ b/docs/fmeflow_run.md @@ -5,28 +5,32 @@ Run a workspace on FME Server. ### Synopsis Run a workspace on FME Server. - - Examples: - # Submit a job asynchronously - fmeflow run --repository Samples --workspace austinApartments.fmw + +``` +fmeflow run [flags] +``` + +### Examples + +``` + + # Submit a job asynchronously + fmeflow run --repository Samples --workspace austinApartments.fmw - # Submit a job and wait for it to complete - fmeflow run --repository Samples --workspace austinApartments.fmw --wait + # Submit a job and wait for it to complete + fmeflow run --repository Samples --workspace austinApartments.fmw --wait - # Submit a job to a specific queue and set a time to live in the queue - fmeflow run --repository Samples --workspace austinApartments.fmw --tag Queue1 --time-to-live 120 + # Submit a job to a specific queue and set a time to live in the queue + fmeflow run --repository Samples --workspace austinApartments.fmw --tag Queue1 --time-to-live 120 - # Submit a job and pass in a few published parameters - fmeflow run --repository Samples --workspace austinDownload.fmw --published-parameter-list THEMES=railroad,airports --published-parameter COORDSYS=TX83-CF + # Submit a job and pass in a few published parameters + fmeflow run --repository Samples --workspace austinDownload.fmw --published-parameter-list THEMES=railroad,airports --published-parameter COORDSYS=TX83-CF - # Submit a job, wait for it to complete, and customize the output - fmeflow run --repository Samples --workspace austinApartments.fmw --wait --output="custom-columns=Time Requested:.timeRequested,Time Started:.timeStarted,Time Finished:.timeFinished" + # Submit a job, wait for it to complete, and customize the output + fmeflow run --repository Samples --workspace austinApartments.fmw --wait --output="custom-columns=Time Requested:.timeRequested,Time Started:.timeStarted,Time Finished:.timeFinished" - # Upload a local file to use as the source data for the translation - fmeflow run --repository Samples --workspace austinApartments.fmw --file Landmarks-edited.sqlite --wait - -``` -fmeflow run [flags] + # Upload a local file to use as the source data for the translation + fmeflow run --repository Samples --workspace austinApartments.fmw --file Landmarks-edited.sqlite --wait ``` ### Options diff --git a/docs/fmeflow_workspaces.md b/docs/fmeflow_workspaces.md index f822e47..8fcc97a 100644 --- a/docs/fmeflow_workspaces.md +++ b/docs/fmeflow_workspaces.md @@ -14,24 +14,23 @@ fmeflow workspaces [flags] ``` - Examples: - # List all workspaces on the FME Server - fmeflow workspaces + # List all workspaces on the FME Server + fmeflow workspaces - # List all workspaces in Samples repository - fmeflow workspaces --repository Samples + # List all workspaces in Samples repository + fmeflow workspaces --repository Samples - # List all workspaces in the Samples repository and output it in json - fmeflow workspaces --repository Samples --json + # List all workspaces in the Samples repository and output it in json + fmeflow workspaces --repository Samples --json - # List all workspaces in the Samples repository with custom columns showing the last publish date and number of times run - fmeflow workspaces --repository Samples --output="custom-columns=NAME:.name,PUBLISH DATE:.lastPublishDate,TOTAL RUNS:.totalRuns" + # List all workspaces in the Samples repository with custom columns showing the last publish date and number of times run + fmeflow workspaces --repository Samples --output="custom-columns=NAME:.name,PUBLISH DATE:.lastPublishDate,TOTAL RUNS:.totalRuns" - # Get information on a single workspace - fmeflow workspaces --repository Samples --name austinApartments.fmw + # Get information on a single workspace + fmeflow workspaces --repository Samples --name austinApartments.fmw - # Get the name, source format, and destination format for this workspace - fmeflow workspaces --repository Samples --name austinApartments.fmw --output=custom-columns=NAME:.name,SOURCE:.datasets.source[*].format,DEST:.datasets.destination[*].format + # Get the name, source format, and destination format for this workspace + fmeflow workspaces --repository Samples --name austinApartments.fmw --output=custom-columns=NAME:.name,SOURCE:.datasets.source[*].format,DEST:.datasets.destination[*].format ``` ### Options From 84f68abb6774d0d7fda5d8fd52e3122190f671e9 Mon Sep 17 00:00:00 2001 From: Grant Arnold Date: Tue, 16 Apr 2024 11:03:59 -0700 Subject: [PATCH 7/7] Update docs/fmeflow.md Co-authored-by: jkerssens <64618997+jkerssens@users.noreply.github.com> --- docs/fmeflow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/fmeflow.md b/docs/fmeflow.md index 96079dc..7af7caa 100644 --- a/docs/fmeflow.md +++ b/docs/fmeflow.md @@ -10,7 +10,7 @@ A command line interface for interacting with FME Server. See available commands ``` # Get started with the login command -fmeserver login https://my-fmeserver.internal +fmeflow login https://my-fmeserver.internal ``` ### Options