From a97ed726cff09f72ca18c1ad89f4512a9acb239a Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Mon, 12 Feb 2024 13:03:02 -0800 Subject: [PATCH 01/13] Use iterators for list APIs --- .codegen/service.go.tmpl | 10 +- bundle/schema/docs/bundle_descriptions.json | 28 +-- cmd/account/billable-usage/billable-usage.go | 2 +- cmd/account/budgets/budgets.go | 6 +- .../custom-app-integration.go | 6 +- cmd/account/groups/groups.go | 6 +- .../ip-access-lists/ip-access-lists.go | 6 +- cmd/account/log-delivery/log-delivery.go | 6 +- .../metastore-assignments.go | 6 +- cmd/account/metastores/metastores.go | 6 +- .../network-connectivity.go | 12 +- .../o-auth-published-apps.go | 6 +- .../published-app-integration.go | 6 +- .../service-principal-secrets.go | 6 +- .../service-principals/service-principals.go | 6 +- cmd/account/users/users.go | 6 +- .../workspace-assignment.go | 6 +- cmd/workspace/catalogs/catalogs.go | 6 +- cmd/workspace/clean-rooms/clean-rooms.go | 6 +- .../cluster-policies/cluster-policies.go | 6 +- cmd/workspace/clusters/clusters.go | 12 +- cmd/workspace/connections/connections.go | 6 +- cmd/workspace/dashboards/dashboards.go | 6 +- cmd/workspace/experiments/experiments.go | 30 ++-- .../external-locations/external-locations.go | 6 +- cmd/workspace/functions/functions.go | 6 +- .../git-credentials/git-credentials.go | 6 +- .../global-init-scripts.go | 6 +- cmd/workspace/groups/groups.go | 6 +- .../instance-pools/instance-pools.go | 6 +- .../instance-profiles/instance-profiles.go | 6 +- .../ip-access-lists/ip-access-lists.go | 6 +- cmd/workspace/jobs/jobs.go | 12 +- cmd/workspace/libraries/libraries.go | 6 +- cmd/workspace/metastores/metastores.go | 6 +- .../model-registry/model-registry.go | 36 ++-- .../model-versions/model-versions.go | 6 +- cmd/workspace/pipelines/pipelines.go | 12 +- .../policy-families/policy-families.go | 6 +- cmd/workspace/providers/providers.go | 12 +- cmd/workspace/queries/queries.go | 6 +- cmd/workspace/query-history/query-history.go | 6 +- cmd/workspace/recipients/recipients.go | 6 +- .../registered-models/registered-models.go | 6 +- cmd/workspace/repos/repos.go | 6 +- cmd/workspace/schemas/schemas.go | 6 +- cmd/workspace/secrets/secrets.go | 18 +- .../service-principals/service-principals.go | 6 +- .../serving-endpoints/serving-endpoints.go | 6 +- cmd/workspace/shares/shares.go | 6 +- .../storage-credentials.go | 6 +- .../system-schemas/system-schemas.go | 6 +- cmd/workspace/tables/tables.go | 12 +- .../token-management/token-management.go | 6 +- cmd/workspace/tokens/tokens.go | 6 +- cmd/workspace/users/users.go | 6 +- .../vector-search-endpoints.go | 6 +- .../vector-search-indexes.go | 6 +- cmd/workspace/volumes/volumes.go | 6 +- cmd/workspace/warehouses/warehouses.go | 6 +- .../workspace-conf/workspace-conf.go | 19 +- cmd/workspace/workspace/workspace.go | 6 +- libs/cmdio/io.go | 42 ----- libs/cmdio/render.go | 167 ++++++++++++++++-- 64 files changed, 343 insertions(+), 375 deletions(-) diff --git a/.codegen/service.go.tmpl b/.codegen/service.go.tmpl index a0cd02198bc..0eb1192e7e8 100644 --- a/.codegen/service.go.tmpl +++ b/.codegen/service.go.tmpl @@ -300,14 +300,20 @@ func init() { // end service {{.Name}}{{end}} {{- define "method-call" -}} - {{if .Response}}response, err :={{else}}err ={{end}} {{if .Service.IsAccounts}}a{{else}}w{{end}}.{{(.Service.TrimPrefix "account").PascalName}}.{{.PascalName}}{{if .Pagination}}All{{end}}(ctx{{if .Request}}, {{.CamelName}}Req{{end}}) + {{if .Response -}} + response{{ if not .Pagination}}, err{{end}} := + {{- else -}} + err = + {{- end}} {{if .Service.IsAccounts}}a{{else}}w{{end}}.{{(.Service.TrimPrefix "account").PascalName}}.{{.PascalName}}(ctx{{if .Request}}, {{.CamelName}}Req{{end}}) + {{if not (and .Response .Pagination) -}} if err != nil { return err } + {{- end}} {{ if .Response -}} {{- if .IsResponseByteStream -}} defer response.{{.ResponseBodyField.PascalName}}.Close() - return cmdio.RenderReader(ctx, response.{{.ResponseBodyField.PascalName}}) + return cmdio.Render(ctx, response.{{.ResponseBodyField.PascalName}}) {{- else -}} return cmdio.Render(ctx, response) {{- end -}} diff --git a/bundle/schema/docs/bundle_descriptions.json b/bundle/schema/docs/bundle_descriptions.json index 8d16970c515..fd25f4300cf 100644 --- a/bundle/schema/docs/bundle_descriptions.json +++ b/bundle/schema/docs/bundle_descriptions.json @@ -182,7 +182,7 @@ "description": "An optional continuous property for this job. The continuous property will ensure that there is always one run executing. Only one of `schedule` and `continuous` can be used.", "properties": { "pause_status": { - "description": "Indicate whether the continuous execution of the job is paused or not. Defaults to UNPAUSED." + "description": "Indicate whether this schedule is paused or not." } } }, @@ -692,7 +692,7 @@ "description": "An optional periodic schedule for this job. The default behavior is that the job only runs when triggered by clicking “Run Now” in the Jobs UI or sending an API request to `runNow`.", "properties": { "pause_status": { - "description": "Indicate whether the continuous execution of the job is paused or not. Defaults to UNPAUSED." + "description": "Indicate whether this schedule is paused or not." }, "quartz_cron_expression": { "description": "A Cron expression using Quartz syntax that describes the schedule for a job.\nSee [Cron Trigger](http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html)\nfor details. This field is required.\"\n" @@ -1210,7 +1210,7 @@ "description": "The path of the notebook to be run in the Databricks workspace or remote repository.\nFor notebooks stored in the Databricks workspace, the path must be absolute and begin with a slash.\nFor notebooks stored in a remote repository, the path must be relative. This field is required.\n" }, "source": { - "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" + "description": "Optional location type of the notebook. When set to `WORKSPACE`, the notebook will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the notebook will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Notebook is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Notebook is located in cloud Git provider.\n" } } }, @@ -1312,7 +1312,7 @@ "description": "The Python file to be executed. Cloud file URIs (such as dbfs:/, s3:/, adls:/, gcs:/) and workspace paths are supported. For python files stored in the Databricks workspace, the path must be absolute and begin with `/`. For files stored in a remote repository, the path must be relative. This field is required." }, "source": { - "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" + "description": "Optional location type of the notebook. When set to `WORKSPACE`, the notebook will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the notebook will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Notebook is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Notebook is located in cloud Git provider.\n" } } }, @@ -1417,7 +1417,7 @@ "description": "An optional timeout applied to each run of this job task. A value of `0` means no timeout." }, "webhook_notifications": { - "description": "A collection of system notification IDs to notify when runs of this job begin or complete.", + "description": "A collection of system notification IDs to notify when runs of this task begin or complete. The default behavior is to not send any system notifications.", "properties": { "on_duration_warning_threshold_exceeded": { "description": "An optional list of system notification IDs to call when the duration of a run exceeds the threshold specified for the `RUN_DURATION_SECONDS` metric in the `health` field. A maximum of 3 destinations can be specified for the `on_duration_warning_threshold_exceeded` property.", @@ -1489,12 +1489,12 @@ } }, "pause_status": { - "description": "Indicate whether the continuous execution of the job is paused or not. Defaults to UNPAUSED." + "description": "Indicate whether this schedule is paused or not." } } }, "webhook_notifications": { - "description": "A collection of system notification IDs to notify when runs of this job begin or complete.", + "description": "A collection of system notification IDs to notify when runs of this task begin or complete. The default behavior is to not send any system notifications.", "properties": { "on_duration_warning_threshold_exceeded": { "description": "An optional list of system notification IDs to call when the duration of a run exceeds the threshold specified for the `RUN_DURATION_SECONDS` metric in the `health` field. A maximum of 3 destinations can be specified for the `on_duration_warning_threshold_exceeded` property.", @@ -2541,7 +2541,7 @@ "description": "An optional continuous property for this job. The continuous property will ensure that there is always one run executing. Only one of `schedule` and `continuous` can be used.", "properties": { "pause_status": { - "description": "Indicate whether the continuous execution of the job is paused or not. Defaults to UNPAUSED." + "description": "Indicate whether this schedule is paused or not." } } }, @@ -3051,7 +3051,7 @@ "description": "An optional periodic schedule for this job. The default behavior is that the job only runs when triggered by clicking “Run Now” in the Jobs UI or sending an API request to `runNow`.", "properties": { "pause_status": { - "description": "Indicate whether the continuous execution of the job is paused or not. Defaults to UNPAUSED." + "description": "Indicate whether this schedule is paused or not." }, "quartz_cron_expression": { "description": "A Cron expression using Quartz syntax that describes the schedule for a job.\nSee [Cron Trigger](http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html)\nfor details. This field is required.\"\n" @@ -3569,7 +3569,7 @@ "description": "The path of the notebook to be run in the Databricks workspace or remote repository.\nFor notebooks stored in the Databricks workspace, the path must be absolute and begin with a slash.\nFor notebooks stored in a remote repository, the path must be relative. This field is required.\n" }, "source": { - "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" + "description": "Optional location type of the notebook. When set to `WORKSPACE`, the notebook will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the notebook will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Notebook is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Notebook is located in cloud Git provider.\n" } } }, @@ -3671,7 +3671,7 @@ "description": "The Python file to be executed. Cloud file URIs (such as dbfs:/, s3:/, adls:/, gcs:/) and workspace paths are supported. For python files stored in the Databricks workspace, the path must be absolute and begin with `/`. For files stored in a remote repository, the path must be relative. This field is required." }, "source": { - "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" + "description": "Optional location type of the notebook. When set to `WORKSPACE`, the notebook will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the notebook will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Notebook is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Notebook is located in cloud Git provider.\n" } } }, @@ -3776,7 +3776,7 @@ "description": "An optional timeout applied to each run of this job task. A value of `0` means no timeout." }, "webhook_notifications": { - "description": "A collection of system notification IDs to notify when runs of this job begin or complete.", + "description": "A collection of system notification IDs to notify when runs of this task begin or complete. The default behavior is to not send any system notifications.", "properties": { "on_duration_warning_threshold_exceeded": { "description": "An optional list of system notification IDs to call when the duration of a run exceeds the threshold specified for the `RUN_DURATION_SECONDS` metric in the `health` field. A maximum of 3 destinations can be specified for the `on_duration_warning_threshold_exceeded` property.", @@ -3848,12 +3848,12 @@ } }, "pause_status": { - "description": "Indicate whether the continuous execution of the job is paused or not. Defaults to UNPAUSED." + "description": "Indicate whether this schedule is paused or not." } } }, "webhook_notifications": { - "description": "A collection of system notification IDs to notify when runs of this job begin or complete.", + "description": "A collection of system notification IDs to notify when runs of this task begin or complete. The default behavior is to not send any system notifications.", "properties": { "on_duration_warning_threshold_exceeded": { "description": "An optional list of system notification IDs to call when the duration of a run exceeds the threshold specified for the `RUN_DURATION_SECONDS` metric in the `health` field. A maximum of 3 destinations can be specified for the `on_duration_warning_threshold_exceeded` property.", diff --git a/cmd/account/billable-usage/billable-usage.go b/cmd/account/billable-usage/billable-usage.go index ec9b7a639cf..bbbc9af2319 100755 --- a/cmd/account/billable-usage/billable-usage.go +++ b/cmd/account/billable-usage/billable-usage.go @@ -92,7 +92,7 @@ func newDownload() *cobra.Command { return err } defer response.Contents.Close() - return cmdio.RenderReader(ctx, response.Contents) + return cmdio.Render(ctx, response.Contents) } // Disable completions since they are not applicable. diff --git a/cmd/account/budgets/budgets.go b/cmd/account/budgets/budgets.go index 69237900e10..43fcb4c976d 100755 --- a/cmd/account/budgets/budgets.go +++ b/cmd/account/budgets/budgets.go @@ -281,10 +281,8 @@ func newList() *cobra.Command { cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { ctx := cmd.Context() a := root.AccountClient(ctx) - response, err := a.Budgets.ListAll(ctx) - if err != nil { - return err - } + response := a.Budgets.List(ctx) + return cmdio.Render(ctx, response) } diff --git a/cmd/account/custom-app-integration/custom-app-integration.go b/cmd/account/custom-app-integration/custom-app-integration.go index e6d216dfcfa..a45935ca89b 100755 --- a/cmd/account/custom-app-integration/custom-app-integration.go +++ b/cmd/account/custom-app-integration/custom-app-integration.go @@ -262,10 +262,8 @@ func newList() *cobra.Command { cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { ctx := cmd.Context() a := root.AccountClient(ctx) - response, err := a.CustomAppIntegration.ListAll(ctx) - if err != nil { - return err - } + response := a.CustomAppIntegration.List(ctx) + return cmdio.Render(ctx, response) } diff --git a/cmd/account/groups/groups.go b/cmd/account/groups/groups.go index ed1fa16426d..528b4b0e132 100755 --- a/cmd/account/groups/groups.go +++ b/cmd/account/groups/groups.go @@ -314,10 +314,8 @@ func newList() *cobra.Command { ctx := cmd.Context() a := root.AccountClient(ctx) - response, err := a.Groups.ListAll(ctx, listReq) - if err != nil { - return err - } + response := a.Groups.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/account/ip-access-lists/ip-access-lists.go b/cmd/account/ip-access-lists/ip-access-lists.go index 20511265d9d..b3d3b723fcf 100755 --- a/cmd/account/ip-access-lists/ip-access-lists.go +++ b/cmd/account/ip-access-lists/ip-access-lists.go @@ -339,10 +339,8 @@ func newList() *cobra.Command { cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { ctx := cmd.Context() a := root.AccountClient(ctx) - response, err := a.IpAccessLists.ListAll(ctx) - if err != nil { - return err - } + response := a.IpAccessLists.List(ctx) + return cmdio.Render(ctx, response) } diff --git a/cmd/account/log-delivery/log-delivery.go b/cmd/account/log-delivery/log-delivery.go index 1846e0fdc9a..9c5df16f871 100755 --- a/cmd/account/log-delivery/log-delivery.go +++ b/cmd/account/log-delivery/log-delivery.go @@ -303,10 +303,8 @@ func newList() *cobra.Command { ctx := cmd.Context() a := root.AccountClient(ctx) - response, err := a.LogDelivery.ListAll(ctx, listReq) - if err != nil { - return err - } + response := a.LogDelivery.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/account/metastore-assignments/metastore-assignments.go b/cmd/account/metastore-assignments/metastore-assignments.go index 619bde50723..0d1d1ef3030 100755 --- a/cmd/account/metastore-assignments/metastore-assignments.go +++ b/cmd/account/metastore-assignments/metastore-assignments.go @@ -294,10 +294,8 @@ func newList() *cobra.Command { listReq.MetastoreId = args[0] - response, err := a.MetastoreAssignments.ListAll(ctx, listReq) - if err != nil { - return err - } + response := a.MetastoreAssignments.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/account/metastores/metastores.go b/cmd/account/metastores/metastores.go index 797bef5ece1..67d7e4c7739 100755 --- a/cmd/account/metastores/metastores.go +++ b/cmd/account/metastores/metastores.go @@ -257,10 +257,8 @@ func newList() *cobra.Command { cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { ctx := cmd.Context() a := root.AccountClient(ctx) - response, err := a.Metastores.ListAll(ctx) - if err != nil { - return err - } + response := a.Metastores.List(ctx) + return cmdio.Render(ctx, response) } diff --git a/cmd/account/network-connectivity/network-connectivity.go b/cmd/account/network-connectivity/network-connectivity.go index 27ab3174360..db6c05c6ecd 100755 --- a/cmd/account/network-connectivity/network-connectivity.go +++ b/cmd/account/network-connectivity/network-connectivity.go @@ -546,10 +546,8 @@ func newListNetworkConnectivityConfigurations() *cobra.Command { ctx := cmd.Context() a := root.AccountClient(ctx) - response, err := a.NetworkConnectivity.ListNetworkConnectivityConfigurationsAll(ctx, listNetworkConnectivityConfigurationsReq) - if err != nil { - return err - } + response := a.NetworkConnectivity.ListNetworkConnectivityConfigurations(ctx, listNetworkConnectivityConfigurationsReq) + return cmdio.Render(ctx, response) } @@ -612,10 +610,8 @@ func newListPrivateEndpointRules() *cobra.Command { listPrivateEndpointRulesReq.NetworkConnectivityConfigId = args[0] - response, err := a.NetworkConnectivity.ListPrivateEndpointRulesAll(ctx, listPrivateEndpointRulesReq) - if err != nil { - return err - } + response := a.NetworkConnectivity.ListPrivateEndpointRules(ctx, listPrivateEndpointRulesReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/account/o-auth-published-apps/o-auth-published-apps.go b/cmd/account/o-auth-published-apps/o-auth-published-apps.go index b611724d4f4..343c052c971 100755 --- a/cmd/account/o-auth-published-apps/o-auth-published-apps.go +++ b/cmd/account/o-auth-published-apps/o-auth-published-apps.go @@ -72,10 +72,8 @@ func newList() *cobra.Command { ctx := cmd.Context() a := root.AccountClient(ctx) - response, err := a.OAuthPublishedApps.ListAll(ctx, listReq) - if err != nil { - return err - } + response := a.OAuthPublishedApps.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/account/published-app-integration/published-app-integration.go b/cmd/account/published-app-integration/published-app-integration.go index d3209c670a6..04ec452c678 100755 --- a/cmd/account/published-app-integration/published-app-integration.go +++ b/cmd/account/published-app-integration/published-app-integration.go @@ -262,10 +262,8 @@ func newList() *cobra.Command { cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { ctx := cmd.Context() a := root.AccountClient(ctx) - response, err := a.PublishedAppIntegration.ListAll(ctx) - if err != nil { - return err - } + response := a.PublishedAppIntegration.List(ctx) + return cmdio.Render(ctx, response) } diff --git a/cmd/account/service-principal-secrets/service-principal-secrets.go b/cmd/account/service-principal-secrets/service-principal-secrets.go index 19d6a491db1..d30764af7a2 100755 --- a/cmd/account/service-principal-secrets/service-principal-secrets.go +++ b/cmd/account/service-principal-secrets/service-principal-secrets.go @@ -226,10 +226,8 @@ func newList() *cobra.Command { return fmt.Errorf("invalid SERVICE_PRINCIPAL_ID: %s", args[0]) } - response, err := a.ServicePrincipalSecrets.ListAll(ctx, listReq) - if err != nil { - return err - } + response := a.ServicePrincipalSecrets.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/account/service-principals/service-principals.go b/cmd/account/service-principals/service-principals.go index 80f1bf4617d..7bff26cfd65 100755 --- a/cmd/account/service-principals/service-principals.go +++ b/cmd/account/service-principals/service-principals.go @@ -313,10 +313,8 @@ func newList() *cobra.Command { ctx := cmd.Context() a := root.AccountClient(ctx) - response, err := a.ServicePrincipals.ListAll(ctx, listReq) - if err != nil { - return err - } + response := a.ServicePrincipals.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/account/users/users.go b/cmd/account/users/users.go index 551766e88b8..d277fb1f174 100755 --- a/cmd/account/users/users.go +++ b/cmd/account/users/users.go @@ -329,10 +329,8 @@ func newList() *cobra.Command { ctx := cmd.Context() a := root.AccountClient(ctx) - response, err := a.Users.ListAll(ctx, listReq) - if err != nil { - return err - } + response := a.Users.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/account/workspace-assignment/workspace-assignment.go b/cmd/account/workspace-assignment/workspace-assignment.go index 7780d90f4a7..d6880a37a5b 100755 --- a/cmd/account/workspace-assignment/workspace-assignment.go +++ b/cmd/account/workspace-assignment/workspace-assignment.go @@ -219,10 +219,8 @@ func newList() *cobra.Command { return fmt.Errorf("invalid WORKSPACE_ID: %s", args[0]) } - response, err := a.WorkspaceAssignment.ListAll(ctx, listReq) - if err != nil { - return err - } + response := a.WorkspaceAssignment.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/catalogs/catalogs.go b/cmd/workspace/catalogs/catalogs.go index 6ffe4a39580..a66c3d80911 100755 --- a/cmd/workspace/catalogs/catalogs.go +++ b/cmd/workspace/catalogs/catalogs.go @@ -292,10 +292,8 @@ func newList() *cobra.Command { cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Catalogs.ListAll(ctx) - if err != nil { - return err - } + response := w.Catalogs.List(ctx) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/clean-rooms/clean-rooms.go b/cmd/workspace/clean-rooms/clean-rooms.go index 99d732f90cd..b100c07dfb8 100755 --- a/cmd/workspace/clean-rooms/clean-rooms.go +++ b/cmd/workspace/clean-rooms/clean-rooms.go @@ -282,10 +282,8 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.CleanRooms.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.CleanRooms.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/cluster-policies/cluster-policies.go b/cmd/workspace/cluster-policies/cluster-policies.go index 339e87c4f38..39e4a15364d 100755 --- a/cmd/workspace/cluster-policies/cluster-policies.go +++ b/cmd/workspace/cluster-policies/cluster-policies.go @@ -602,10 +602,8 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.ClusterPolicies.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.ClusterPolicies.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/clusters/clusters.go b/cmd/workspace/clusters/clusters.go index d159ffd7b87..3dc9221f448 100755 --- a/cmd/workspace/clusters/clusters.go +++ b/cmd/workspace/clusters/clusters.go @@ -652,10 +652,8 @@ func newEvents() *cobra.Command { eventsReq.ClusterId = args[0] } - response, err := w.Clusters.EventsAll(ctx, eventsReq) - if err != nil { - return err - } + response := w.Clusters.Events(ctx, eventsReq) + return cmdio.Render(ctx, response) } @@ -956,10 +954,8 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Clusters.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.Clusters.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/connections/connections.go b/cmd/workspace/connections/connections.go index 5ad0c199b82..08bbaa2adc0 100755 --- a/cmd/workspace/connections/connections.go +++ b/cmd/workspace/connections/connections.go @@ -293,10 +293,8 @@ func newList() *cobra.Command { cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Connections.ListAll(ctx) - if err != nil { - return err - } + response := w.Connections.List(ctx) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/dashboards/dashboards.go b/cmd/workspace/dashboards/dashboards.go index 3346a5e015c..1e95aca7ac5 100755 --- a/cmd/workspace/dashboards/dashboards.go +++ b/cmd/workspace/dashboards/dashboards.go @@ -290,10 +290,8 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Dashboards.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.Dashboards.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/experiments/experiments.go b/cmd/workspace/experiments/experiments.go index 79828714cc2..c4b1d594acd 100755 --- a/cmd/workspace/experiments/experiments.go +++ b/cmd/workspace/experiments/experiments.go @@ -731,10 +731,8 @@ func newGetHistory() *cobra.Command { getHistoryReq.MetricKey = args[0] - response, err := w.Experiments.GetHistoryAll(ctx, getHistoryReq) - if err != nil { - return err - } + response := w.Experiments.GetHistory(ctx, getHistoryReq) + return cmdio.Render(ctx, response) } @@ -996,10 +994,8 @@ func newListArtifacts() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Experiments.ListArtifactsAll(ctx, listArtifactsReq) - if err != nil { - return err - } + response := w.Experiments.ListArtifacts(ctx, listArtifactsReq) + return cmdio.Render(ctx, response) } @@ -1059,10 +1055,8 @@ func newListExperiments() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Experiments.ListExperimentsAll(ctx, listExperimentsReq) - if err != nil { - return err - } + response := w.Experiments.ListExperiments(ctx, listExperimentsReq) + return cmdio.Render(ctx, response) } @@ -1838,10 +1832,8 @@ func newSearchExperiments() *cobra.Command { } } - response, err := w.Experiments.SearchExperimentsAll(ctx, searchExperimentsReq) - if err != nil { - return err - } + response := w.Experiments.SearchExperiments(ctx, searchExperimentsReq) + return cmdio.Render(ctx, response) } @@ -1915,10 +1907,8 @@ func newSearchRuns() *cobra.Command { } } - response, err := w.Experiments.SearchRunsAll(ctx, searchRunsReq) - if err != nil { - return err - } + response := w.Experiments.SearchRuns(ctx, searchRunsReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/external-locations/external-locations.go b/cmd/workspace/external-locations/external-locations.go index b4166086d38..aefdb0266d1 100755 --- a/cmd/workspace/external-locations/external-locations.go +++ b/cmd/workspace/external-locations/external-locations.go @@ -319,10 +319,8 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.ExternalLocations.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.ExternalLocations.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/functions/functions.go b/cmd/workspace/functions/functions.go index 35356be0fde..2b84f8d3bd9 100755 --- a/cmd/workspace/functions/functions.go +++ b/cmd/workspace/functions/functions.go @@ -327,10 +327,8 @@ func newList() *cobra.Command { listReq.CatalogName = args[0] listReq.SchemaName = args[1] - response, err := w.Functions.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.Functions.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/git-credentials/git-credentials.go b/cmd/workspace/git-credentials/git-credentials.go index ca256564c9d..4300e30eb2a 100755 --- a/cmd/workspace/git-credentials/git-credentials.go +++ b/cmd/workspace/git-credentials/git-credentials.go @@ -311,10 +311,8 @@ func newList() *cobra.Command { cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.GitCredentials.ListAll(ctx) - if err != nil { - return err - } + response := w.GitCredentials.List(ctx) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/global-init-scripts/global-init-scripts.go b/cmd/workspace/global-init-scripts/global-init-scripts.go index 1479381da1a..1e4da9c84a4 100755 --- a/cmd/workspace/global-init-scripts/global-init-scripts.go +++ b/cmd/workspace/global-init-scripts/global-init-scripts.go @@ -309,10 +309,8 @@ func newList() *cobra.Command { cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.GlobalInitScripts.ListAll(ctx) - if err != nil { - return err - } + response := w.GlobalInitScripts.List(ctx) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/groups/groups.go b/cmd/workspace/groups/groups.go index 588bce3164c..7b98086e93b 100755 --- a/cmd/workspace/groups/groups.go +++ b/cmd/workspace/groups/groups.go @@ -314,10 +314,8 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Groups.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.Groups.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/instance-pools/instance-pools.go b/cmd/workspace/instance-pools/instance-pools.go index 968f64bc694..d1105c32160 100755 --- a/cmd/workspace/instance-pools/instance-pools.go +++ b/cmd/workspace/instance-pools/instance-pools.go @@ -602,10 +602,8 @@ func newList() *cobra.Command { cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.InstancePools.ListAll(ctx) - if err != nil { - return err - } + response := w.InstancePools.List(ctx) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/instance-profiles/instance-profiles.go b/cmd/workspace/instance-profiles/instance-profiles.go index ca78a15f24a..b4d0331c467 100755 --- a/cmd/workspace/instance-profiles/instance-profiles.go +++ b/cmd/workspace/instance-profiles/instance-profiles.go @@ -251,10 +251,8 @@ func newList() *cobra.Command { cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.InstanceProfiles.ListAll(ctx) - if err != nil { - return err - } + response := w.InstanceProfiles.List(ctx) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/ip-access-lists/ip-access-lists.go b/cmd/workspace/ip-access-lists/ip-access-lists.go index 5bba8b51dbe..49bb4d4a697 100755 --- a/cmd/workspace/ip-access-lists/ip-access-lists.go +++ b/cmd/workspace/ip-access-lists/ip-access-lists.go @@ -340,10 +340,8 @@ func newList() *cobra.Command { cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.IpAccessLists.ListAll(ctx) - if err != nil { - return err - } + response := w.IpAccessLists.List(ctx) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/jobs/jobs.go b/cmd/workspace/jobs/jobs.go index 634a7f3999c..6e48601002d 100755 --- a/cmd/workspace/jobs/jobs.go +++ b/cmd/workspace/jobs/jobs.go @@ -1042,10 +1042,8 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Jobs.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.Jobs.List(ctx, listReq) + return cmdio.Render(ctx, response) } @@ -1112,10 +1110,8 @@ func newListRuns() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Jobs.ListRunsAll(ctx, listRunsReq) - if err != nil { - return err - } + response := w.Jobs.ListRuns(ctx, listRunsReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/libraries/libraries.go b/cmd/workspace/libraries/libraries.go index 1e742892deb..82806421e51 100755 --- a/cmd/workspace/libraries/libraries.go +++ b/cmd/workspace/libraries/libraries.go @@ -157,10 +157,8 @@ func newClusterStatus() *cobra.Command { clusterStatusReq.ClusterId = args[0] - response, err := w.Libraries.ClusterStatusAll(ctx, clusterStatusReq) - if err != nil { - return err - } + response := w.Libraries.ClusterStatus(ctx, clusterStatusReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/metastores/metastores.go b/cmd/workspace/metastores/metastores.go index a0e03ad0d5a..c42f1b0ae09 100755 --- a/cmd/workspace/metastores/metastores.go +++ b/cmd/workspace/metastores/metastores.go @@ -455,10 +455,8 @@ func newList() *cobra.Command { cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Metastores.ListAll(ctx) - if err != nil { - return err - } + response := w.Metastores.List(ctx) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/model-registry/model-registry.go b/cmd/workspace/model-registry/model-registry.go index fade898ec08..fc303b66bfa 100755 --- a/cmd/workspace/model-registry/model-registry.go +++ b/cmd/workspace/model-registry/model-registry.go @@ -1128,10 +1128,8 @@ func newGetLatestVersions() *cobra.Command { getLatestVersionsReq.Name = args[0] } - response, err := w.ModelRegistry.GetLatestVersionsAll(ctx, getLatestVersionsReq) - if err != nil { - return err - } + response := w.ModelRegistry.GetLatestVersions(ctx, getLatestVersionsReq) + return cmdio.Render(ctx, response) } @@ -1520,10 +1518,8 @@ func newListModels() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.ModelRegistry.ListModelsAll(ctx, listModelsReq) - if err != nil { - return err - } + response := w.ModelRegistry.ListModels(ctx, listModelsReq) + return cmdio.Render(ctx, response) } @@ -1586,10 +1582,8 @@ func newListTransitionRequests() *cobra.Command { listTransitionRequestsReq.Name = args[0] listTransitionRequestsReq.Version = args[1] - response, err := w.ModelRegistry.ListTransitionRequestsAll(ctx, listTransitionRequestsReq) - if err != nil { - return err - } + response := w.ModelRegistry.ListTransitionRequests(ctx, listTransitionRequestsReq) + return cmdio.Render(ctx, response) } @@ -1651,10 +1645,8 @@ func newListWebhooks() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.ModelRegistry.ListWebhooksAll(ctx, listWebhooksReq) - if err != nil { - return err - } + response := w.ModelRegistry.ListWebhooks(ctx, listWebhooksReq) + return cmdio.Render(ctx, response) } @@ -1900,10 +1892,8 @@ func newSearchModelVersions() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.ModelRegistry.SearchModelVersionsAll(ctx, searchModelVersionsReq) - if err != nil { - return err - } + response := w.ModelRegistry.SearchModelVersions(ctx, searchModelVersionsReq) + return cmdio.Render(ctx, response) } @@ -1964,10 +1954,8 @@ func newSearchModels() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.ModelRegistry.SearchModelsAll(ctx, searchModelsReq) - if err != nil { - return err - } + response := w.ModelRegistry.SearchModels(ctx, searchModelsReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/model-versions/model-versions.go b/cmd/workspace/model-versions/model-versions.go index 97438264e22..743bd836242 100755 --- a/cmd/workspace/model-versions/model-versions.go +++ b/cmd/workspace/model-versions/model-versions.go @@ -315,10 +315,8 @@ func newList() *cobra.Command { listReq.FullName = args[0] - response, err := w.ModelVersions.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.ModelVersions.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/pipelines/pipelines.go b/cmd/workspace/pipelines/pipelines.go index d35eb3cd80f..501ccbc2a06 100755 --- a/cmd/workspace/pipelines/pipelines.go +++ b/cmd/workspace/pipelines/pipelines.go @@ -536,10 +536,8 @@ func newListPipelineEvents() *cobra.Command { } listPipelineEventsReq.PipelineId = args[0] - response, err := w.Pipelines.ListPipelineEventsAll(ctx, listPipelineEventsReq) - if err != nil { - return err - } + response := w.Pipelines.ListPipelineEvents(ctx, listPipelineEventsReq) + return cmdio.Render(ctx, response) } @@ -600,10 +598,8 @@ func newListPipelines() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Pipelines.ListPipelinesAll(ctx, listPipelinesReq) - if err != nil { - return err - } + response := w.Pipelines.ListPipelines(ctx, listPipelinesReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/policy-families/policy-families.go b/cmd/workspace/policy-families/policy-families.go index 75ab862a7bc..d76e0a15353 100755 --- a/cmd/workspace/policy-families/policy-families.go +++ b/cmd/workspace/policy-families/policy-families.go @@ -138,10 +138,8 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.PolicyFamilies.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.PolicyFamilies.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/providers/providers.go b/cmd/workspace/providers/providers.go index 851c668a715..b127a26ef0f 100755 --- a/cmd/workspace/providers/providers.go +++ b/cmd/workspace/providers/providers.go @@ -323,10 +323,8 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Providers.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.Providers.List(ctx, listReq) + return cmdio.Render(ctx, response) } @@ -401,10 +399,8 @@ func newListShares() *cobra.Command { } listSharesReq.Name = args[0] - response, err := w.Providers.ListSharesAll(ctx, listSharesReq) - if err != nil { - return err - } + response := w.Providers.ListShares(ctx, listSharesReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/queries/queries.go b/cmd/workspace/queries/queries.go index 38fa9c0c510..53634efeb22 100755 --- a/cmd/workspace/queries/queries.go +++ b/cmd/workspace/queries/queries.go @@ -300,10 +300,8 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Queries.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.Queries.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/query-history/query-history.go b/cmd/workspace/query-history/query-history.go index 337ab403331..39305ae1477 100755 --- a/cmd/workspace/query-history/query-history.go +++ b/cmd/workspace/query-history/query-history.go @@ -73,10 +73,8 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.QueryHistory.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.QueryHistory.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/recipients/recipients.go b/cmd/workspace/recipients/recipients.go index 463d7985c70..d701a748fbf 100755 --- a/cmd/workspace/recipients/recipients.go +++ b/cmd/workspace/recipients/recipients.go @@ -342,10 +342,8 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Recipients.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.Recipients.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/registered-models/registered-models.go b/cmd/workspace/registered-models/registered-models.go index 774859f1741..679aac06cd0 100755 --- a/cmd/workspace/registered-models/registered-models.go +++ b/cmd/workspace/registered-models/registered-models.go @@ -450,10 +450,8 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.RegisteredModels.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.RegisteredModels.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/repos/repos.go b/cmd/workspace/repos/repos.go index 62f63750297..9f4aadd2707 100755 --- a/cmd/workspace/repos/repos.go +++ b/cmd/workspace/repos/repos.go @@ -485,10 +485,8 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Repos.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.Repos.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/schemas/schemas.go b/cmd/workspace/schemas/schemas.go index bad61a5f152..046b366bae4 100755 --- a/cmd/workspace/schemas/schemas.go +++ b/cmd/workspace/schemas/schemas.go @@ -333,10 +333,8 @@ func newList() *cobra.Command { listReq.CatalogName = args[0] - response, err := w.Schemas.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.Schemas.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/secrets/secrets.go b/cmd/workspace/secrets/secrets.go index 270538b007a..958b704dc77 100755 --- a/cmd/workspace/secrets/secrets.go +++ b/cmd/workspace/secrets/secrets.go @@ -590,10 +590,8 @@ func newListAcls() *cobra.Command { listAclsReq.Scope = args[0] - response, err := w.Secrets.ListAclsAll(ctx, listAclsReq) - if err != nil { - return err - } + response := w.Secrets.ListAcls(ctx, listAclsReq) + return cmdio.Render(ctx, response) } @@ -641,10 +639,8 @@ func newListScopes() *cobra.Command { cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Secrets.ListScopesAll(ctx) - if err != nil { - return err - } + response := w.Secrets.ListScopes(ctx) + return cmdio.Render(ctx, response) } @@ -712,10 +708,8 @@ func newListSecrets() *cobra.Command { listSecretsReq.Scope = args[0] - response, err := w.Secrets.ListSecretsAll(ctx, listSecretsReq) - if err != nil { - return err - } + response := w.Secrets.ListSecrets(ctx, listSecretsReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/service-principals/service-principals.go b/cmd/workspace/service-principals/service-principals.go index 5e66804d118..1e141653be5 100755 --- a/cmd/workspace/service-principals/service-principals.go +++ b/cmd/workspace/service-principals/service-principals.go @@ -313,10 +313,8 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.ServicePrincipals.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.ServicePrincipals.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/serving-endpoints/serving-endpoints.go b/cmd/workspace/serving-endpoints/serving-endpoints.go index 8c488d093e6..2fc8006a9db 100755 --- a/cmd/workspace/serving-endpoints/serving-endpoints.go +++ b/cmd/workspace/serving-endpoints/serving-endpoints.go @@ -543,10 +543,8 @@ func newList() *cobra.Command { cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.ServingEndpoints.ListAll(ctx) - if err != nil { - return err - } + response := w.ServingEndpoints.List(ctx) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/shares/shares.go b/cmd/workspace/shares/shares.go index 7cb85abfb80..e014d89d05e 100755 --- a/cmd/workspace/shares/shares.go +++ b/cmd/workspace/shares/shares.go @@ -281,10 +281,8 @@ func newList() *cobra.Command { cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Shares.ListAll(ctx) - if err != nil { - return err - } + response := w.Shares.List(ctx) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/storage-credentials/storage-credentials.go b/cmd/workspace/storage-credentials/storage-credentials.go index 910d2b5df5c..dcb0eb7c0b7 100755 --- a/cmd/workspace/storage-credentials/storage-credentials.go +++ b/cmd/workspace/storage-credentials/storage-credentials.go @@ -336,10 +336,8 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.StorageCredentials.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.StorageCredentials.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/system-schemas/system-schemas.go b/cmd/workspace/system-schemas/system-schemas.go index 6dbad5a3f91..d46b25104f6 100755 --- a/cmd/workspace/system-schemas/system-schemas.go +++ b/cmd/workspace/system-schemas/system-schemas.go @@ -216,10 +216,8 @@ func newList() *cobra.Command { listReq.MetastoreId = args[0] - response, err := w.SystemSchemas.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.SystemSchemas.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/tables/tables.go b/cmd/workspace/tables/tables.go index e655dfd7ceb..f1d08960686 100755 --- a/cmd/workspace/tables/tables.go +++ b/cmd/workspace/tables/tables.go @@ -257,10 +257,8 @@ func newList() *cobra.Command { listReq.CatalogName = args[0] listReq.SchemaName = args[1] - response, err := w.Tables.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.Tables.List(ctx, listReq) + return cmdio.Render(ctx, response) } @@ -348,10 +346,8 @@ func newListSummaries() *cobra.Command { } listSummariesReq.CatalogName = args[0] - response, err := w.Tables.ListSummariesAll(ctx, listSummariesReq) - if err != nil { - return err - } + response := w.Tables.ListSummaries(ctx, listSummariesReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/token-management/token-management.go b/cmd/workspace/token-management/token-management.go index d4616e0bdb4..a1ec7f878a2 100755 --- a/cmd/workspace/token-management/token-management.go +++ b/cmd/workspace/token-management/token-management.go @@ -413,10 +413,8 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.TokenManagement.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.TokenManagement.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/tokens/tokens.go b/cmd/workspace/tokens/tokens.go index cd82ef63ff9..357f51f1ca5 100755 --- a/cmd/workspace/tokens/tokens.go +++ b/cmd/workspace/tokens/tokens.go @@ -232,10 +232,8 @@ func newList() *cobra.Command { cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Tokens.ListAll(ctx) - if err != nil { - return err - } + response := w.Tokens.List(ctx) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/users/users.go b/cmd/workspace/users/users.go index 4cc485e96ca..7d831b701ae 100755 --- a/cmd/workspace/users/users.go +++ b/cmd/workspace/users/users.go @@ -426,10 +426,8 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Users.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.Users.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/vector-search-endpoints/vector-search-endpoints.go b/cmd/workspace/vector-search-endpoints/vector-search-endpoints.go index d429267ad2e..e79101a6614 100755 --- a/cmd/workspace/vector-search-endpoints/vector-search-endpoints.go +++ b/cmd/workspace/vector-search-endpoints/vector-search-endpoints.go @@ -308,10 +308,8 @@ func newListEndpoints() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.VectorSearchEndpoints.ListEndpointsAll(ctx, listEndpointsReq) - if err != nil { - return err - } + response := w.VectorSearchEndpoints.ListEndpoints(ctx, listEndpointsReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/vector-search-indexes/vector-search-indexes.go b/cmd/workspace/vector-search-indexes/vector-search-indexes.go index 8999967f14b..bd890d5b1c5 100755 --- a/cmd/workspace/vector-search-indexes/vector-search-indexes.go +++ b/cmd/workspace/vector-search-indexes/vector-search-indexes.go @@ -389,10 +389,8 @@ func newListIndexes() *cobra.Command { listIndexesReq.EndpointName = args[0] - response, err := w.VectorSearchIndexes.ListIndexesAll(ctx, listIndexesReq) - if err != nil { - return err - } + response := w.VectorSearchIndexes.ListIndexes(ctx, listIndexesReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/volumes/volumes.go b/cmd/workspace/volumes/volumes.go index 77b601819c0..0b6ed98cddc 100755 --- a/cmd/workspace/volumes/volumes.go +++ b/cmd/workspace/volumes/volumes.go @@ -292,10 +292,8 @@ func newList() *cobra.Command { listReq.CatalogName = args[0] listReq.SchemaName = args[1] - response, err := w.Volumes.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.Volumes.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/warehouses/warehouses.go b/cmd/workspace/warehouses/warehouses.go index c64788b8962..2d39e979bbe 100755 --- a/cmd/workspace/warehouses/warehouses.go +++ b/cmd/workspace/warehouses/warehouses.go @@ -661,10 +661,8 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) - response, err := w.Warehouses.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.Warehouses.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/workspace-conf/workspace-conf.go b/cmd/workspace/workspace-conf/workspace-conf.go index 687c31adc6d..99207ffadc1 100755 --- a/cmd/workspace/workspace-conf/workspace-conf.go +++ b/cmd/workspace/workspace-conf/workspace-conf.go @@ -3,8 +3,11 @@ package workspace_conf import ( + "fmt" + "github.com/databricks/cli/cmd/root" "github.com/databricks/cli/libs/cmdio" + "github.com/databricks/cli/libs/flags" "github.com/databricks/databricks-sdk-go/service/settings" "github.com/spf13/cobra" ) @@ -106,8 +109,10 @@ func newSetStatus() *cobra.Command { cmd := &cobra.Command{} var setStatusReq settings.WorkspaceConf + var setStatusJson flags.JsonFlag // TODO: short flags + cmd.Flags().Var(&setStatusJson, "json", `either inline JSON string or @path/to/file.json with request body`) cmd.Use = "set-status" cmd.Short = `Enable/disable features.` @@ -118,16 +123,20 @@ func newSetStatus() *cobra.Command { cmd.Annotations = make(map[string]string) - cmd.Args = func(cmd *cobra.Command, args []string) error { - check := cobra.ExactArgs(0) - return check(cmd, args) - } - cmd.PreRunE = root.MustWorkspaceClient cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { ctx := cmd.Context() w := root.WorkspaceClient(ctx) + if cmd.Flags().Changed("json") { + err = setStatusJson.Unmarshal(&setStatusReq) + if err != nil { + return err + } + } else { + return fmt.Errorf("please provide command input in JSON format by specifying the --json flag") + } + err = w.WorkspaceConf.SetStatus(ctx, setStatusReq) if err != nil { return err diff --git a/cmd/workspace/workspace/workspace.go b/cmd/workspace/workspace/workspace.go index 5777f22fe97..e863a3d5782 100755 --- a/cmd/workspace/workspace/workspace.go +++ b/cmd/workspace/workspace/workspace.go @@ -577,10 +577,8 @@ func newList() *cobra.Command { listReq.Path = args[0] - response, err := w.Workspace.ListAll(ctx, listReq) - if err != nil { - return err - } + response := w.Workspace.List(ctx, listReq) + return cmdio.Render(ctx, response) } diff --git a/libs/cmdio/io.go b/libs/cmdio/io.go index d20991a7c23..2a799361a64 100644 --- a/libs/cmdio/io.go +++ b/libs/cmdio/io.go @@ -113,48 +113,6 @@ func IsGitBash(ctx context.Context) bool { return false } -func Render(ctx context.Context, v any) error { - c := fromContext(ctx) - return RenderWithTemplate(ctx, v, c.template) -} - -func RenderWithTemplate(ctx context.Context, v any, template string) error { - // TODO: add terminal width & white/dark theme detection - c := fromContext(ctx) - switch c.outputFormat { - case flags.OutputJSON: - return renderJson(c.out, v) - case flags.OutputText: - if template != "" { - return renderTemplate(c.out, template, v) - } - return renderJson(c.out, v) - default: - return fmt.Errorf("invalid output format: %s", c.outputFormat) - } -} - -func RenderJson(ctx context.Context, v any) error { - c := fromContext(ctx) - if c.outputFormat == flags.OutputJSON { - return renderJson(c.out, v) - } - return nil -} - -func RenderReader(ctx context.Context, r io.Reader) error { - c := fromContext(ctx) - switch c.outputFormat { - case flags.OutputJSON: - return fmt.Errorf("json output not supported") - case flags.OutputText: - _, err := io.Copy(c.out, r) - return err - default: - return fmt.Errorf("invalid output format: %s", c.outputFormat) - } -} - type Tuple struct{ Name, Id string } func (c *cmdIO) Select(items []Tuple, label string) (id string, err error) { diff --git a/libs/cmdio/render.go b/libs/cmdio/render.go index d641f61dfdd..78688616182 100644 --- a/libs/cmdio/render.go +++ b/libs/cmdio/render.go @@ -2,14 +2,18 @@ package cmdio import ( "bytes" + "context" "encoding/base64" "encoding/json" + "fmt" "io" + "reflect" "strings" "text/tabwriter" "text/template" "time" + "github.com/databricks/cli/libs/flags" "github.com/fatih/color" "github.com/nwidger/jsoncolor" ) @@ -46,20 +50,161 @@ func Heredoc(tmpl string) (trimmed string) { return strings.TrimSpace(trimmed) } -func renderJson(w io.Writer, v any) error { - pretty, err := fancyJSON(v) - if err != nil { - return err +type renderer struct { + renderTemplate func(context.Context, *template.Template, io.Writer) error + renderText func(context.Context, io.Writer) error + renderJson func(context.Context, io.Writer) error +} + +type reflectIterator struct { + hasNext reflect.Value + next reflect.Value +} + +func newReflectIterator(v any) (reflectIterator, bool) { + rv := reflect.ValueOf(v) + rt := rv.Type() + _, hasHasNext := rt.MethodByName("HasNext") + _, hasNext := rt.MethodByName("Next") + if hasNext && hasHasNext { + return reflectIterator{ + hasNext: rv.MethodByName("HasNext"), + next: rv.MethodByName("Next"), + }, true } - _, err = w.Write(pretty) - if err != nil { - return err + return reflectIterator{}, false +} + +func (r reflectIterator) HasNext(ctx context.Context) bool { + res := r.hasNext.Call([]reflect.Value{reflect.ValueOf(ctx)}) + return res[0].Bool() +} + +func (r reflectIterator) Next(ctx context.Context) (any, error) { + res := r.next.Call([]reflect.Value{reflect.ValueOf(ctx)}) + item := res[0].Interface() + if res[1].IsNil() { + return item, nil + } + return item, res[1].Interface().(error) +} + +func New(it any) *renderer { + if r, ok := any(it).(io.Reader); ok { + return &renderer{ + renderJson: func(_ context.Context, w io.Writer) error { + return fmt.Errorf("json output not supported") + }, + renderText: func(_ context.Context, w io.Writer) error { + _, err := io.Copy(w, r) + return err + }, + } } - _, err = w.Write([]byte("\n")) - return err + + if iterator, ok := newReflectIterator(it); ok { + return &renderer{ + renderJson: func(ctx context.Context, w io.Writer) error { + // Iterators are always rendered as a list of resources in JSON. + _, err := w.Write([]byte("[\n")) + if err != nil { + return err + } + for iterator.HasNext(ctx) { + n, err := iterator.Next(ctx) + if err != nil { + return err + } + res, err := json.MarshalIndent(n, " ", " ") + if err != nil { + return err + } + _, err = w.Write(res) + if err != nil { + return err + } + } + _, err = w.Write([]byte("]\n")) + if err != nil { + return err + } + return nil + }, + renderTemplate: func(ctx context.Context, t *template.Template, w io.Writer) error { + for iterator.HasNext(ctx) { + n, err := iterator.Next(ctx) + if err != nil { + return err + } + err = t.Execute(w, []any{n}) + if err != nil { + return err + } + } + return nil + }, + } + } + return &renderer{ + renderJson: func(_ context.Context, w io.Writer) error { + pretty, err := fancyJSON(it) + if err != nil { + return err + } + _, err = w.Write(pretty) + if err != nil { + return err + } + _, err = w.Write([]byte("\n")) + if err != nil { + return err + } + return nil + }, + renderTemplate: func(_ context.Context, t *template.Template, w io.Writer) error { + return t.Execute(w, it) + }, + } +} + +func (r *renderer) renderWithTemplate(ctx context.Context, template string) error { + // TODO: add terminal width & white/dark theme detection + c := fromContext(ctx) + switch c.outputFormat { + case flags.OutputJSON: + return r.renderJson(ctx, c.out) + case flags.OutputText: + if r.renderTemplate != nil && template != "" { + return r.renderUsingTemplate(ctx, c.out, template) + } + if r.renderText != nil { + return r.renderText(ctx, c.out) + } + return r.renderJson(ctx, c.out) + default: + return fmt.Errorf("invalid output format: %s", c.outputFormat) + } +} + +func Render(ctx context.Context, v any) error { + c := fromContext(ctx) + return RenderWithTemplate(ctx, v, c.template) +} + +func RenderWithTemplate(ctx context.Context, v any, template string) error { + return New(v).renderWithTemplate(ctx, template) +} + +func RenderJson(ctx context.Context, v any) error { + c := fromContext(ctx) + return New(v).renderJson(ctx, c.out) +} + +func RenderReader(ctx context.Context, r io.Reader) error { + return New(r).renderWithTemplate(ctx, "") } -func renderTemplate(w io.Writer, tmpl string, v any) error { +func (r *renderer) renderUsingTemplate(ctx context.Context, w io.Writer, tmpl string) error { tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0) t, err := template.New("command").Funcs(template.FuncMap{ // we render colored output if stdout is TTY, otherwise we render text. @@ -120,7 +265,7 @@ func renderTemplate(w io.Writer, tmpl string, v any) error { if err != nil { return err } - err = t.Execute(tw, v) + err = r.renderTemplate(ctx, t, tw) if err != nil { return err } From a7e5ad0e3c2d8496b612c026ffde8d12f2ffd14b Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Mon, 12 Feb 2024 14:04:38 -0800 Subject: [PATCH 02/13] work --- libs/cmdio/reflect_iterator.go | 39 +++++ libs/cmdio/render.go | 266 ++++++++++++++++++++------------- 2 files changed, 199 insertions(+), 106 deletions(-) create mode 100644 libs/cmdio/reflect_iterator.go diff --git a/libs/cmdio/reflect_iterator.go b/libs/cmdio/reflect_iterator.go new file mode 100644 index 00000000000..d27715ebb0a --- /dev/null +++ b/libs/cmdio/reflect_iterator.go @@ -0,0 +1,39 @@ +package cmdio + +import ( + "context" + "reflect" +) + +type reflectIterator struct { + hasNext reflect.Value + next reflect.Value +} + +func newReflectIterator(v any) (reflectIterator, bool) { + rv := reflect.ValueOf(v) + rt := rv.Type() + _, hasHasNext := rt.MethodByName("HasNext") + _, hasNext := rt.MethodByName("Next") + if hasNext && hasHasNext { + return reflectIterator{ + hasNext: rv.MethodByName("HasNext"), + next: rv.MethodByName("Next"), + }, true + } + return reflectIterator{}, false +} + +func (r reflectIterator) HasNext(ctx context.Context) bool { + res := r.hasNext.Call([]reflect.Value{reflect.ValueOf(ctx)}) + return res[0].Bool() +} + +func (r reflectIterator) Next(ctx context.Context) (any, error) { + res := r.next.Call([]reflect.Value{reflect.ValueOf(ctx)}) + item := res[0].Interface() + if res[1].IsNil() { + return item, nil + } + return item, res[1].Interface().(error) +} diff --git a/libs/cmdio/render.go b/libs/cmdio/render.go index 78688616182..08ca6ee7a7c 100644 --- a/libs/cmdio/render.go +++ b/libs/cmdio/render.go @@ -5,9 +5,9 @@ import ( "context" "encoding/base64" "encoding/json" + "errors" "fmt" "io" - "reflect" "strings" "text/tabwriter" "text/template" @@ -50,137 +50,184 @@ func Heredoc(tmpl string) (trimmed string) { return strings.TrimSpace(trimmed) } -type renderer struct { - renderTemplate func(context.Context, *template.Template, io.Writer) error - renderText func(context.Context, io.Writer) error - renderJson func(context.Context, io.Writer) error +type writeFlusher interface { + io.Writer + Flush() error } -type reflectIterator struct { - hasNext reflect.Value - next reflect.Value +type jsonRenderer interface { + renderJson(context.Context, writeFlusher) error } -func newReflectIterator(v any) (reflectIterator, bool) { - rv := reflect.ValueOf(v) - rt := rv.Type() - _, hasHasNext := rt.MethodByName("HasNext") - _, hasNext := rt.MethodByName("Next") - if hasNext && hasHasNext { - return reflectIterator{ - hasNext: rv.MethodByName("HasNext"), - next: rv.MethodByName("Next"), - }, true - } - return reflectIterator{}, false +type textRenderer interface { + renderText(context.Context, writeFlusher) error +} + +type templateRenderer interface { + renderTemplate(context.Context, *template.Template, writeFlusher) error } -func (r reflectIterator) HasNext(ctx context.Context) bool { - res := r.hasNext.Call([]reflect.Value{reflect.ValueOf(ctx)}) - return res[0].Bool() +type readerRenderer struct { + reader io.Reader } -func (r reflectIterator) Next(ctx context.Context) (any, error) { - res := r.next.Call([]reflect.Value{reflect.ValueOf(ctx)}) - item := res[0].Interface() - if res[1].IsNil() { - return item, nil +func (r readerRenderer) renderText(_ context.Context, w writeFlusher) error { + _, err := io.Copy(w, r.reader) + if err != nil { + return err } - return item, res[1].Interface().(error) + return w.Flush() } -func New(it any) *renderer { - if r, ok := any(it).(io.Reader); ok { - return &renderer{ - renderJson: func(_ context.Context, w io.Writer) error { - return fmt.Errorf("json output not supported") - }, - renderText: func(_ context.Context, w io.Writer) error { - _, err := io.Copy(w, r) - return err - }, - } +type iteratorRenderer struct { + t reflectIterator + bufferSize int +} + +func (ir iteratorRenderer) getBufferSize() int { + if ir.bufferSize == 0 { + return 100 } + return ir.bufferSize +} - if iterator, ok := newReflectIterator(it); ok { - return &renderer{ - renderJson: func(ctx context.Context, w io.Writer) error { - // Iterators are always rendered as a list of resources in JSON. - _, err := w.Write([]byte("[\n")) - if err != nil { - return err - } - for iterator.HasNext(ctx) { - n, err := iterator.Next(ctx) - if err != nil { - return err - } - res, err := json.MarshalIndent(n, " ", " ") - if err != nil { - return err - } - _, err = w.Write(res) - if err != nil { - return err - } - } - _, err = w.Write([]byte("]\n")) - if err != nil { - return err - } - return nil - }, - renderTemplate: func(ctx context.Context, t *template.Template, w io.Writer) error { - for iterator.HasNext(ctx) { - n, err := iterator.Next(ctx) - if err != nil { - return err - } - err = t.Execute(w, []any{n}) - if err != nil { - return err - } - } - return nil - }, - } +func (ir iteratorRenderer) renderJson(ctx context.Context, w writeFlusher) error { + // Iterators are always rendered as a list of resources in JSON. + _, err := w.Write([]byte("[\n ")) + if err != nil { + return err } - return &renderer{ - renderJson: func(_ context.Context, w io.Writer) error { - pretty, err := fancyJSON(it) - if err != nil { - return err - } - _, err = w.Write(pretty) + for i := 0; ir.t.HasNext(ctx); i++ { + n, err := ir.t.Next(ctx) + if err != nil { + return err + } + res, err := json.MarshalIndent(n, " ", " ") + if err != nil { + return err + } + _, err = w.Write(res) + if err != nil { + return err + } + _, err = w.Write([]byte(",\n ")) + if err != nil { + return err + } + if (i+1)%ir.getBufferSize() == 0 { + err = w.Flush() if err != nil { return err } - _, err = w.Write([]byte("\n")) + } + } + _, err = w.Write([]byte("]\n")) + if err != nil { + return err + } + return w.Flush() +} + +func (ir iteratorRenderer) renderTemplate(ctx context.Context, t *template.Template, w writeFlusher) error { + for i := 0; ir.t.HasNext(ctx); i++ { + n, err := ir.t.Next(ctx) + if err != nil { + return err + } + err = t.Execute(w, []any{n}) + if err != nil { + return err + } + if (i+1)%ir.getBufferSize() == 0 { + err = w.Flush() if err != nil { return err } - return nil - }, - renderTemplate: func(_ context.Context, t *template.Template, w io.Writer) error { - return t.Execute(w, it) - }, + } } + return w.Flush() +} + +type defaultRenderer struct { + it any } -func (r *renderer) renderWithTemplate(ctx context.Context, template string) error { +func (d defaultRenderer) renderJson(_ context.Context, w writeFlusher) error { + pretty, err := fancyJSON(d.it) + if err != nil { + return err + } + _, err = w.Write(pretty) + if err != nil { + return err + } + _, err = w.Write([]byte("\n")) + if err != nil { + return err + } + return nil +} + +func (d defaultRenderer) renderTemplate(_ context.Context, t *template.Template, w writeFlusher) error { + return t.Execute(w, d.it) +} + +// Returns something implementing one of the following interfaces: +// - jsonRenderer +// - textRenderer +// - templateRenderer +func newRenderer(it any) any { + if r, ok := any(it).(io.Reader); ok { + return readerRenderer{reader: r} + } + if iterator, ok := newReflectIterator(it); ok { + return iteratorRenderer{t: iterator} + } + return defaultRenderer{it: it} +} + +type bufferedFlusher struct { + w io.Writer + b bytes.Buffer +} + +func (b bufferedFlusher) Write(bs []byte) (int, error) { + return b.w.Write(bs) +} + +func (b bufferedFlusher) Flush() error { + _, err := b.Write(b.b.Bytes()) + if err != nil { + return err + } + b.b.Reset() + return nil +} + +func newBufferedFlusher(w io.Writer) writeFlusher { + return bufferedFlusher{w: w} +} + +func renderWithTemplate(r any, ctx context.Context, template string) error { // TODO: add terminal width & white/dark theme detection c := fromContext(ctx) switch c.outputFormat { case flags.OutputJSON: - return r.renderJson(ctx, c.out) + if jr, ok := r.(jsonRenderer); ok { + return jr.renderJson(ctx, newBufferedFlusher(c.out)) + } + return errors.New("json output not supported") case flags.OutputText: - if r.renderTemplate != nil && template != "" { - return r.renderUsingTemplate(ctx, c.out, template) + if tr, ok := r.(templateRenderer); ok && template != "" { + return renderUsingTemplate(ctx, tr, c.out, template) } - if r.renderText != nil { - return r.renderText(ctx, c.out) + if tr, ok := r.(textRenderer); ok { + return tr.renderText(ctx, newBufferedFlusher(c.out)) } - return r.renderJson(ctx, c.out) + if jr, ok := r.(jsonRenderer); ok { + return jr.renderJson(ctx, newBufferedFlusher(c.out)) + } + return errors.New("no renderer defined") default: return fmt.Errorf("invalid output format: %s", c.outputFormat) } @@ -192,19 +239,26 @@ func Render(ctx context.Context, v any) error { } func RenderWithTemplate(ctx context.Context, v any, template string) error { - return New(v).renderWithTemplate(ctx, template) + return renderWithTemplate(newRenderer(v), ctx, template) } func RenderJson(ctx context.Context, v any) error { c := fromContext(ctx) - return New(v).renderJson(ctx, c.out) + if jr, ok := newRenderer(v).(jsonRenderer); ok { + jr.renderJson(ctx, newBufferedFlusher(c.out)) + } + return errors.New("json output not supported") } func RenderReader(ctx context.Context, r io.Reader) error { - return New(r).renderWithTemplate(ctx, "") + c := fromContext(ctx) + if jr, ok := newRenderer(r).(textRenderer); ok { + jr.renderText(ctx, newBufferedFlusher(c.out)) + } + return errors.New("rendering io.Reader not supported") } -func (r *renderer) renderUsingTemplate(ctx context.Context, w io.Writer, tmpl string) error { +func renderUsingTemplate(ctx context.Context, r templateRenderer, w io.Writer, tmpl string) error { tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0) t, err := template.New("command").Funcs(template.FuncMap{ // we render colored output if stdout is TTY, otherwise we render text. From 14d311ecea5e2765a4a14e5ee05e8d22f840aad8 Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Mon, 12 Feb 2024 15:14:51 -0800 Subject: [PATCH 03/13] add test coverage --- cmd/fs/cp.go | 4 +- cmd/fs/ls.go | 4 +- cmd/labs/project/proxy.go | 2 +- cmd/root/io.go | 5 +- cmd/workspace/catalogs/overrides.go | 3 +- cmd/workspace/clusters/overrides.go | 3 +- cmd/workspace/dashboards/overrides.go | 3 +- cmd/workspace/external-locations/overrides.go | 3 +- cmd/workspace/jobs/overrides.go | 3 +- cmd/workspace/metastores/overrides.go | 3 +- cmd/workspace/queries/overrides.go | 3 +- cmd/workspace/schemas/overrides.go | 3 +- cmd/workspace/secrets/overrides.go | 6 +- .../storage-credentials/overrides.go | 3 +- cmd/workspace/tables/overrides.go | 3 +- cmd/workspace/token-management/overrides.go | 3 +- cmd/workspace/tokens/overrides.go | 3 +- cmd/workspace/warehouses/overrides.go | 3 +- cmd/workspace/workspace/export_dir.go | 4 +- cmd/workspace/workspace/import_dir.go | 4 +- cmd/workspace/workspace/overrides.go | 3 +- internal/bundle/helpers.go | 2 +- libs/cmdio/io.go | 28 ++-- libs/cmdio/reflect_iterator.go | 10 ++ libs/cmdio/reflect_iterator_test.go | 50 +++++++ libs/cmdio/render.go | 46 ++++-- libs/cmdio/render_test.go | 133 ++++++++++++++++++ .../databrickscfg/cfgpickers/clusters_test.go | 4 +- libs/template/helpers_test.go | 2 +- 29 files changed, 289 insertions(+), 57 deletions(-) create mode 100644 libs/cmdio/reflect_iterator_test.go create mode 100644 libs/cmdio/render_test.go diff --git a/cmd/fs/cp.go b/cmd/fs/cp.go index 97fceb93cb3..e889f6e974e 100644 --- a/cmd/fs/cp.go +++ b/cmd/fs/cp.go @@ -107,7 +107,7 @@ func (c *copy) emitFileSkippedEvent(sourcePath, targetPath string) error { event := newFileSkippedEvent(fullSourcePath, fullTargetPath) template := "{{.SourcePath}} -> {{.TargetPath}} (skipped; already exists)\n" - return cmdio.RenderWithTemplate(c.ctx, event, template) + return cmdio.RenderWithTemplate(c.ctx, event, "", template) } func (c *copy) emitFileCopiedEvent(sourcePath, targetPath string) error { @@ -123,7 +123,7 @@ func (c *copy) emitFileCopiedEvent(sourcePath, targetPath string) error { event := newFileCopiedEvent(fullSourcePath, fullTargetPath) template := "{{.SourcePath}} -> {{.TargetPath}}\n" - return cmdio.RenderWithTemplate(c.ctx, event, template) + return cmdio.RenderWithTemplate(c.ctx, event, "", template) } func newCpCommand() *cobra.Command { diff --git a/cmd/fs/ls.go b/cmd/fs/ls.go index 7ae55e1f466..8bbf828ce2d 100644 --- a/cmd/fs/ls.go +++ b/cmd/fs/ls.go @@ -78,12 +78,12 @@ func newLsCommand() *cobra.Command { // Use template for long mode if the flag is set if long { - return cmdio.RenderWithTemplate(ctx, jsonDirEntries, cmdio.Heredoc(` + return cmdio.RenderWithTemplate(ctx, jsonDirEntries, "", cmdio.Heredoc(` {{range .}}{{if .IsDir}}DIRECTORY {{else}}FILE {{end}}{{.Size}} {{.ModTime|pretty_date}} {{.Name}} {{end}} `)) } - return cmdio.RenderWithTemplate(ctx, jsonDirEntries, cmdio.Heredoc(` + return cmdio.RenderWithTemplate(ctx, jsonDirEntries, "", cmdio.Heredoc(` {{range .}}{{.Name}} {{end}} `)) diff --git a/cmd/labs/project/proxy.go b/cmd/labs/project/proxy.go index d872560a569..ee1b0aa9199 100644 --- a/cmd/labs/project/proxy.go +++ b/cmd/labs/project/proxy.go @@ -87,7 +87,7 @@ func (cp *proxy) renderJsonAsTable(cmd *cobra.Command, args []string, envs map[s } // IntelliJ eagerly replaces tabs with spaces, even though we're not asking for it fixedTemplate := strings.ReplaceAll(cp.TableTemplate, "\\t", "\t") - return cmdio.RenderWithTemplate(ctx, anyVal, fixedTemplate) + return cmdio.RenderWithTemplate(ctx, anyVal, "", fixedTemplate) } func (cp *proxy) commandInput(cmd *cobra.Command) ([]string, error) { diff --git a/cmd/root/io.go b/cmd/root/io.go index 23c7d6c642f..b224bbb27a0 100644 --- a/cmd/root/io.go +++ b/cmd/root/io.go @@ -38,13 +38,14 @@ func OutputType(cmd *cobra.Command) flags.Output { } func (f *outputFlag) initializeIO(cmd *cobra.Command) error { - var template string + var headerTemplate, template string if cmd.Annotations != nil { // rely on zeroval being an empty string template = cmd.Annotations["template"] + headerTemplate = cmd.Annotations["headerTemplate"] } - cmdIO := cmdio.NewIO(f.output, cmd.InOrStdin(), cmd.OutOrStdout(), cmd.ErrOrStderr(), template) + cmdIO := cmdio.NewIO(f.output, cmd.InOrStdin(), cmd.OutOrStdout(), cmd.ErrOrStderr(), headerTemplate, template) ctx := cmdio.InContext(cmd.Context(), cmdIO) cmd.SetContext(ctx) return nil diff --git a/cmd/workspace/catalogs/overrides.go b/cmd/workspace/catalogs/overrides.go index 6de7a7771bc..9ab1bf05282 100644 --- a/cmd/workspace/catalogs/overrides.go +++ b/cmd/workspace/catalogs/overrides.go @@ -6,8 +6,9 @@ import ( ) func listOverride(listCmd *cobra.Command) { + listCmd.Annotations["headerTemplate"] = cmdio.Heredoc(` + {{header "Name"}} {{header "Type"}} {{header "Comment"}}`) listCmd.Annotations["template"] = cmdio.Heredoc(` - {{header "Name"}} {{header "Type"}} {{header "Comment"}} {{range .}}{{.Name|green}} {{blue "%s" .CatalogType}} {{.Comment}} {{end}}`) } diff --git a/cmd/workspace/clusters/overrides.go b/cmd/workspace/clusters/overrides.go index ab32a4cd8a4..55976d406ae 100644 --- a/cmd/workspace/clusters/overrides.go +++ b/cmd/workspace/clusters/overrides.go @@ -7,8 +7,9 @@ import ( ) func listOverride(listCmd *cobra.Command, _ *compute.ListClustersRequest) { + listCmd.Annotations["headerTemplate"] = cmdio.Heredoc(` + {{header "ID"}} {{header "Name"}} {{header "State"}}`) listCmd.Annotations["template"] = cmdio.Heredoc(` - {{header "ID"}} {{header "Name"}} {{header "State"}} {{range .}}{{.ClusterId | green}} {{.ClusterName | cyan}} {{if eq .State "RUNNING"}}{{green "%s" .State}}{{else if eq .State "TERMINATED"}}{{red "%s" .State}}{{else}}{{blue "%s" .State}}{{end}} {{end}}`) } diff --git a/cmd/workspace/dashboards/overrides.go b/cmd/workspace/dashboards/overrides.go index 709e657f857..6a26ebbfbcd 100644 --- a/cmd/workspace/dashboards/overrides.go +++ b/cmd/workspace/dashboards/overrides.go @@ -7,8 +7,9 @@ import ( ) func listOverride(listCmd *cobra.Command, _ *sql.ListDashboardsRequest) { + listCmd.Annotations["headerTemplate"] = cmdio.Heredoc(` + {{header "ID"}} {{header "Name"}}`) listCmd.Annotations["template"] = cmdio.Heredoc(` - {{header "ID"}} {{header "Name"}} {{range .}}{{.Id|green}} {{.Name}} {{end}}`) } diff --git a/cmd/workspace/external-locations/overrides.go b/cmd/workspace/external-locations/overrides.go index 63a30cfc3e6..00b4921d4dc 100644 --- a/cmd/workspace/external-locations/overrides.go +++ b/cmd/workspace/external-locations/overrides.go @@ -7,8 +7,9 @@ import ( ) func listOverride(listCmd *cobra.Command, listReq *catalog.ListExternalLocationsRequest) { + listCmd.Annotations["headerTemplate"] = cmdio.Heredoc(` + {{header "Name"}} {{header "Credential"}} {{header "URL"}}`) listCmd.Annotations["template"] = cmdio.Heredoc(` - {{header "Name"}} {{header "Credential"}} {{header "URL"}} {{range .}}{{.Name|green}} {{.CredentialName|cyan}} {{.Url}} {{end}}`) } diff --git a/cmd/workspace/jobs/overrides.go b/cmd/workspace/jobs/overrides.go index fd22dcbdb1d..ee7d205517c 100644 --- a/cmd/workspace/jobs/overrides.go +++ b/cmd/workspace/jobs/overrides.go @@ -13,8 +13,9 @@ func listOverride(listCmd *cobra.Command, listReq *jobs.ListJobsRequest) { } func listRunsOverride(listRunsCmd *cobra.Command, listRunsReq *jobs.ListRunsRequest) { + listRunsCmd.Annotations["headerTemplate"] = cmdio.Heredoc(` + {{header "Job ID"}} {{header "Run ID"}} {{header "Result State"}} URL`) listRunsCmd.Annotations["template"] = cmdio.Heredoc(` - {{header "Job ID"}} {{header "Run ID"}} {{header "Result State"}} URL {{range .}}{{green "%d" .JobId}} {{cyan "%d" .RunId}} {{if eq .State.ResultState "SUCCESS"}}{{"SUCCESS"|green}}{{else}}{{red "%s" .State.ResultState}}{{end}} {{.RunPageUrl}} {{end}}`) } diff --git a/cmd/workspace/metastores/overrides.go b/cmd/workspace/metastores/overrides.go index 2c9ca6f79a3..3ee6a107148 100644 --- a/cmd/workspace/metastores/overrides.go +++ b/cmd/workspace/metastores/overrides.go @@ -6,8 +6,9 @@ import ( ) func listOverride(listCmd *cobra.Command) { + listCmd.Annotations["headerTemplate"] = cmdio.Heredoc(` + {{header "ID"}} {{header "Name"}} {{"Region"}}`) listCmd.Annotations["template"] = cmdio.Heredoc(` - {{header "ID"}} {{header "Name"}} {{"Region"}} {{range .}}{{.MetastoreId|green}} {{.Name|cyan}} {{.Region}} {{end}}`) } diff --git a/cmd/workspace/queries/overrides.go b/cmd/workspace/queries/overrides.go index a06dabdeba5..d7edf93a02d 100644 --- a/cmd/workspace/queries/overrides.go +++ b/cmd/workspace/queries/overrides.go @@ -8,8 +8,9 @@ import ( func listOverride(listCmd *cobra.Command, listReq *sql.ListQueriesRequest) { // TODO: figure out colored/non-colored headers and colspan shifts + listCmd.Annotations["headerTemplate"] = cmdio.Heredoc(` + {{header "ID"}} {{header "Name"}} {{header "Author"}}`) listCmd.Annotations["template"] = cmdio.Heredoc(` - {{header "ID"}} {{header "Name"}} {{header "Author"}} {{range .}}{{.Id|green}} {{.Name|cyan}} {{.User.Email|cyan}} {{end}}`) } diff --git a/cmd/workspace/schemas/overrides.go b/cmd/workspace/schemas/overrides.go index 180690b6e10..ba4c65ce735 100644 --- a/cmd/workspace/schemas/overrides.go +++ b/cmd/workspace/schemas/overrides.go @@ -7,8 +7,9 @@ import ( ) func listOverride(listCmd *cobra.Command, listReq *catalog.ListSchemasRequest) { + listCmd.Annotations["headerTemplate"] = cmdio.Heredoc(` + {{header "Full Name"}} {{header "Owner"}} {{header "Comment"}}`) listCmd.Annotations["template"] = cmdio.Heredoc(` - {{header "Full Name"}} {{header "Owner"}} {{header "Comment"}} {{range .}}{{.FullName|green}} {{.Owner|cyan}} {{.Comment}} {{end}}`) } diff --git a/cmd/workspace/secrets/overrides.go b/cmd/workspace/secrets/overrides.go index 6e765bf73f9..b215f17a7f7 100644 --- a/cmd/workspace/secrets/overrides.go +++ b/cmd/workspace/secrets/overrides.go @@ -11,15 +11,17 @@ func cmdOverride(cmd *cobra.Command) { } func listScopesOverride(listScopesCmd *cobra.Command) { + listScopesCmd.Annotations["headerTemplate"] = cmdio.Heredoc(` + {{header "Scope"}} {{header "Backend Type"}}`) listScopesCmd.Annotations["template"] = cmdio.Heredoc(` - {{header "Scope"}} {{header "Backend Type"}} {{range .}}{{.Name|green}} {{.BackendType}} {{end}}`) } func listSecretsOverride(listSecretsCommand *cobra.Command, _ *workspace.ListSecretsRequest) { + listSecretsCommand.Annotations["headerTemplate"] = cmdio.Heredoc(` + {{header "Key"}} {{header "Last Updated Timestamp"}}`) listSecretsCommand.Annotations["template"] = cmdio.Heredoc(` - {{header "Key"}} {{header "Last Updated Timestamp"}} {{range .}}{{.Key|green}} {{.LastUpdatedTimestamp}} {{end}}`) } diff --git a/cmd/workspace/storage-credentials/overrides.go b/cmd/workspace/storage-credentials/overrides.go index 534e045ddbe..92dec91ebc3 100644 --- a/cmd/workspace/storage-credentials/overrides.go +++ b/cmd/workspace/storage-credentials/overrides.go @@ -7,8 +7,9 @@ import ( ) func listOverride(listCmd *cobra.Command, listReq *catalog.ListStorageCredentialsRequest) { + listCmd.Annotations["headerTemplate"] = cmdio.Heredoc(` + {{header "ID"}} {{header "Name"}} {{header "Credentials"}}`) listCmd.Annotations["template"] = cmdio.Heredoc(` - {{header "ID"}} {{header "Name"}} {{header "Credentials"}} {{range .}}{{.Id|green}} {{.Name|cyan}} {{if .AwsIamRole}}{{.AwsIamRole.RoleArn}}{{end}}{{if .AzureServicePrincipal}}{{.AzureServicePrincipal.ApplicationId}}{{end}}{{if .DatabricksGcpServiceAccount}}{{.DatabricksGcpServiceAccount.Email}}{{end}} {{end}}`) } diff --git a/cmd/workspace/tables/overrides.go b/cmd/workspace/tables/overrides.go index 35fc351a479..a0849ada7f4 100644 --- a/cmd/workspace/tables/overrides.go +++ b/cmd/workspace/tables/overrides.go @@ -7,8 +7,9 @@ import ( ) func listOverride(listCmd *cobra.Command, listReq *catalog.ListTablesRequest) { + listCmd.Annotations["headerTemplate"] = cmdio.Heredoc(` + {{header "Full Name"}} {{header "Table Type"}}`) listCmd.Annotations["template"] = cmdio.Heredoc(` - {{header "Full Name"}} {{header "Table Type"}} {{range .}}{{.FullName|green}} {{blue "%s" .TableType}} {{end}}`) } diff --git a/cmd/workspace/token-management/overrides.go b/cmd/workspace/token-management/overrides.go index 46967d37a08..8122c1a1b39 100644 --- a/cmd/workspace/token-management/overrides.go +++ b/cmd/workspace/token-management/overrides.go @@ -7,8 +7,9 @@ import ( ) func listOverride(listCmd *cobra.Command, listReq *settings.ListTokenManagementRequest) { + listCmd.Annotations["headerTemplate"] = cmdio.Heredoc(` + {{header "ID"}} {{header "Created By"}} {{header "Comment"}}`) listCmd.Annotations["template"] = cmdio.Heredoc(` - {{header "ID"}} {{header "Created By"}} {{header "Comment"}} {{range .}}{{.TokenId|green}} {{.CreatedByUsername|cyan}} {{.Comment|cyan}} {{end}}`) } diff --git a/cmd/workspace/tokens/overrides.go b/cmd/workspace/tokens/overrides.go index 09c51758e6c..142902da4fa 100644 --- a/cmd/workspace/tokens/overrides.go +++ b/cmd/workspace/tokens/overrides.go @@ -6,8 +6,9 @@ import ( ) func listOverride(listCmd *cobra.Command) { + listCmd.Annotations["headerTemplate"] = cmdio.Heredoc(` + {{header "ID"}} {{header "Expiry time"}} {{header "Comment"}}`) listCmd.Annotations["template"] = cmdio.Heredoc(` - {{header "ID"}} {{header "Expiry time"}} {{header "Comment"}} {{range .}}{{.TokenId|green}} {{cyan "%d" .ExpiryTime}} {{.Comment|cyan}} {{end}}`) } diff --git a/cmd/workspace/warehouses/overrides.go b/cmd/workspace/warehouses/overrides.go index 0714937c259..9457557d00b 100644 --- a/cmd/workspace/warehouses/overrides.go +++ b/cmd/workspace/warehouses/overrides.go @@ -7,8 +7,9 @@ import ( ) func listOverride(listCmd *cobra.Command, listReq *sql.ListWarehousesRequest) { + listCmd.Annotations["headerTemplate"] = cmdio.Heredoc(` + {{header "ID"}} {{header "Name"}} {{header "Size"}} {{header "State"}}`) listCmd.Annotations["template"] = cmdio.Heredoc(` - {{header "ID"}} {{header "Name"}} {{header "Size"}} {{header "State"}} {{range .}}{{.Id|green}} {{.Name|cyan}} {{.ClusterSize|cyan}} {{if eq .State "RUNNING"}}{{"RUNNING"|green}}{{else if eq .State "STOPPED"}}{{"STOPPED"|red}}{{else}}{{blue "%s" .State}}{{end}} {{end}}`) } diff --git a/cmd/workspace/workspace/export_dir.go b/cmd/workspace/workspace/export_dir.go index d2a86d009af..79e64e8ad25 100644 --- a/cmd/workspace/workspace/export_dir.go +++ b/cmd/workspace/workspace/export_dir.go @@ -55,7 +55,7 @@ func (opts exportDirOptions) callback(ctx context.Context, workspaceFiler filer. // If a file exists, and overwrite is not set, we skip exporting the file if _, err := os.Stat(targetPath); err == nil && !overwrite { // Log event that this file/directory has been skipped - return cmdio.RenderWithTemplate(ctx, newFileSkippedEvent(relPath, targetPath), "{{.SourcePath}} -> {{.TargetPath}} (skipped; already exists)\n") + return cmdio.RenderWithTemplate(ctx, newFileSkippedEvent(relPath, targetPath), "", "{{.SourcePath}} -> {{.TargetPath}} (skipped; already exists)\n") } // create the file @@ -74,7 +74,7 @@ func (opts exportDirOptions) callback(ctx context.Context, workspaceFiler filer. if err != nil { return err } - return cmdio.RenderWithTemplate(ctx, newFileExportedEvent(sourcePath, targetPath), "{{.SourcePath}} -> {{.TargetPath}}\n") + return cmdio.RenderWithTemplate(ctx, newFileExportedEvent(sourcePath, targetPath), "", "{{.SourcePath}} -> {{.TargetPath}}\n") } } diff --git a/cmd/workspace/workspace/import_dir.go b/cmd/workspace/workspace/import_dir.go index bc0b80667c5..6ce5f3c2b43 100644 --- a/cmd/workspace/workspace/import_dir.go +++ b/cmd/workspace/workspace/import_dir.go @@ -93,14 +93,14 @@ func (opts importDirOptions) callback(ctx context.Context, workspaceFiler filer. // Emit file skipped event with the appropriate template fileSkippedEvent := newFileSkippedEvent(localName, path.Join(targetDir, remoteName)) template := "{{.SourcePath}} -> {{.TargetPath}} (skipped; already exists)\n" - return cmdio.RenderWithTemplate(ctx, fileSkippedEvent, template) + return cmdio.RenderWithTemplate(ctx, fileSkippedEvent, "", template) } if err != nil { return err } } fileImportedEvent := newFileImportedEvent(localName, path.Join(targetDir, remoteName)) - return cmdio.RenderWithTemplate(ctx, fileImportedEvent, "{{.SourcePath}} -> {{.TargetPath}}\n") + return cmdio.RenderWithTemplate(ctx, fileImportedEvent, "", "{{.SourcePath}} -> {{.TargetPath}}\n") } } diff --git a/cmd/workspace/workspace/overrides.go b/cmd/workspace/workspace/overrides.go index 1cac67419fa..cfed0a6eec0 100644 --- a/cmd/workspace/workspace/overrides.go +++ b/cmd/workspace/workspace/overrides.go @@ -17,8 +17,9 @@ import ( func listOverride(listCmd *cobra.Command, listReq *workspace.ListWorkspaceRequest) { listReq.Path = "/" + listCmd.Annotations["headerTemplate"] = cmdio.Heredoc(` + {{header "ID"}} {{header "Type"}} {{header "Language"}} {{header "Path"}}`) listCmd.Annotations["template"] = cmdio.Heredoc(` - {{header "ID"}} {{header "Type"}} {{header "Language"}} {{header "Path"}} {{range .}}{{green "%d" .ObjectId}} {{blue "%s" .ObjectType}} {{cyan "%s" .Language}} {{.Path|cyan}} {{end}}`) } diff --git a/internal/bundle/helpers.go b/internal/bundle/helpers.go index 2c2b2dac943..a8fbd230ef3 100644 --- a/internal/bundle/helpers.go +++ b/internal/bundle/helpers.go @@ -25,7 +25,7 @@ func initTestTemplate(t *testing.T, ctx context.Context, templateName string, co } ctx = root.SetWorkspaceClient(ctx, nil) - cmd := cmdio.NewIO(flags.OutputJSON, strings.NewReader(""), os.Stdout, os.Stderr, "bundles") + cmd := cmdio.NewIO(flags.OutputJSON, strings.NewReader(""), os.Stdout, os.Stderr, "", "bundles") ctx = cmdio.InContext(ctx, cmd) err = template.Materialize(ctx, configFilePath, templateRoot, bundleRoot) diff --git a/libs/cmdio/io.go b/libs/cmdio/io.go index 2a799361a64..75c0c4b8750 100644 --- a/libs/cmdio/io.go +++ b/libs/cmdio/io.go @@ -22,27 +22,29 @@ import ( type cmdIO struct { // states if we are in the interactive mode // e.g. if stdout is a terminal - interactive bool - outputFormat flags.Output - template string - in io.Reader - out io.Writer - err io.Writer + interactive bool + outputFormat flags.Output + headerTemplate string + template string + in io.Reader + out io.Writer + err io.Writer } -func NewIO(outputFormat flags.Output, in io.Reader, out io.Writer, err io.Writer, template string) *cmdIO { +func NewIO(outputFormat flags.Output, in io.Reader, out io.Writer, err io.Writer, headerTemplate, template string) *cmdIO { // The check below is similar to color.NoColor but uses the specified err writer. dumb := os.Getenv("NO_COLOR") != "" || os.Getenv("TERM") == "dumb" if f, ok := err.(*os.File); ok && !dumb { dumb = !isatty.IsTerminal(f.Fd()) && !isatty.IsCygwinTerminal(f.Fd()) } return &cmdIO{ - interactive: !dumb, - outputFormat: outputFormat, - template: template, - in: in, - out: out, - err: err, + interactive: !dumb, + outputFormat: outputFormat, + headerTemplate: headerTemplate, + template: template, + in: in, + out: out, + err: err, } } diff --git a/libs/cmdio/reflect_iterator.go b/libs/cmdio/reflect_iterator.go index d27715ebb0a..af47dc7ee50 100644 --- a/libs/cmdio/reflect_iterator.go +++ b/libs/cmdio/reflect_iterator.go @@ -3,8 +3,16 @@ package cmdio import ( "context" "reflect" + + "github.com/databricks/databricks-sdk-go/listing" ) +// Reflectively call Next and HasNext on listing.Iterator[*] values. +// +// Because listing.Iterator[T] has a type parameter, it isn't possible to +// use a normal switch statement to inspect whether a value implements this +// interface for some T. Instead, we resort to checking whether the provided +// object has HasNext() and Next() methods. type reflectIterator struct { hasNext reflect.Value next reflect.Value @@ -37,3 +45,5 @@ func (r reflectIterator) Next(ctx context.Context) (any, error) { } return item, res[1].Interface().(error) } + +var _ listing.Iterator[any] = reflectIterator{} diff --git a/libs/cmdio/reflect_iterator_test.go b/libs/cmdio/reflect_iterator_test.go new file mode 100644 index 00000000000..2c82e74de83 --- /dev/null +++ b/libs/cmdio/reflect_iterator_test.go @@ -0,0 +1,50 @@ +package cmdio + +import ( + "context" + "errors" + "testing" + + "github.com/databricks/databricks-sdk-go/listing" + "github.com/stretchr/testify/assert" +) + +type dummyIterator struct { + items []any +} + +func (d *dummyIterator) HasNext(_ context.Context) bool { + return len(d.items) > 0 +} + +func (d *dummyIterator) Next(ctx context.Context) (any, error) { + if !d.HasNext(ctx) { + return nil, errors.New("no more items") + } + item := d.items[0] + d.items = d.items[1:] + return item, nil +} + +var _ listing.Iterator[any] = &dummyIterator{} + +func TestReflectIterator_NonIterator(t *testing.T) { + _, ok := newReflectIterator(3) + assert.False(t, ok) +} + +func TestReflectIterator_DummyIterator(t *testing.T) { + ri, ok := newReflectIterator(&dummyIterator{items: []any{1, "2", true}}) + assert.True(t, ok) + ctx := context.Background() + first, err := ri.Next(ctx) + assert.NoError(t, err) + assert.Equal(t, first, 1) + second, err := ri.Next(ctx) + assert.NoError(t, err) + assert.Equal(t, second, "2") + third, err := ri.Next(ctx) + assert.NoError(t, err) + assert.Equal(t, third, true) + assert.False(t, ri.HasNext(ctx)) +} diff --git a/libs/cmdio/render.go b/libs/cmdio/render.go index 08ca6ee7a7c..235a24acfbd 100644 --- a/libs/cmdio/render.go +++ b/libs/cmdio/render.go @@ -50,20 +50,25 @@ func Heredoc(tmpl string) (trimmed string) { return strings.TrimSpace(trimmed) } +// writeFlusher represents a buffered writer that can be flushed. This is useful when +// buffering writing a large number of resources (such as during a list API). type writeFlusher interface { io.Writer Flush() error } type jsonRenderer interface { + // Render an object as JSON to the provided writeFlusher. renderJson(context.Context, writeFlusher) error } type textRenderer interface { + // Render an object as text to the provided writeFlusher. renderText(context.Context, writeFlusher) error } type templateRenderer interface { + // Render an object using the provided template and write to the provided writeFlusher. renderTemplate(context.Context, *template.Template, writeFlusher) error } @@ -98,6 +103,12 @@ func (ir iteratorRenderer) renderJson(ctx context.Context, w writeFlusher) error return err } for i := 0; ir.t.HasNext(ctx); i++ { + if i != 0 { + _, err = w.Write([]byte(",\n ")) + if err != nil { + return err + } + } n, err := ir.t.Next(ctx) if err != nil { return err @@ -110,10 +121,6 @@ func (ir iteratorRenderer) renderJson(ctx context.Context, w writeFlusher) error if err != nil { return err } - _, err = w.Write([]byte(",\n ")) - if err != nil { - return err - } if (i+1)%ir.getBufferSize() == 0 { err = w.Flush() if err != nil { @@ -121,7 +128,7 @@ func (ir iteratorRenderer) renderJson(ctx context.Context, w writeFlusher) error } } } - _, err = w.Write([]byte("]\n")) + _, err = w.Write([]byte("\n]\n")) if err != nil { return err } @@ -208,7 +215,7 @@ func newBufferedFlusher(w io.Writer) writeFlusher { return bufferedFlusher{w: w} } -func renderWithTemplate(r any, ctx context.Context, template string) error { +func renderWithTemplate(r any, ctx context.Context, headerTemplate, template string) error { // TODO: add terminal width & white/dark theme detection c := fromContext(ctx) switch c.outputFormat { @@ -219,7 +226,7 @@ func renderWithTemplate(r any, ctx context.Context, template string) error { return errors.New("json output not supported") case flags.OutputText: if tr, ok := r.(templateRenderer); ok && template != "" { - return renderUsingTemplate(ctx, tr, c.out, template) + return renderUsingTemplate(ctx, tr, c.out, headerTemplate, template) } if tr, ok := r.(textRenderer); ok { return tr.renderText(ctx, newBufferedFlusher(c.out)) @@ -235,11 +242,11 @@ func renderWithTemplate(r any, ctx context.Context, template string) error { func Render(ctx context.Context, v any) error { c := fromContext(ctx) - return RenderWithTemplate(ctx, v, c.template) + return renderWithTemplate(newRenderer(v), ctx, c.headerTemplate, c.template) } -func RenderWithTemplate(ctx context.Context, v any, template string) error { - return renderWithTemplate(newRenderer(v), ctx, template) +func RenderWithTemplate(ctx context.Context, v any, headerTemplate, template string) error { + return renderWithTemplate(newRenderer(v), ctx, headerTemplate, template) } func RenderJson(ctx context.Context, v any) error { @@ -258,9 +265,9 @@ func RenderReader(ctx context.Context, r io.Reader) error { return errors.New("rendering io.Reader not supported") } -func renderUsingTemplate(ctx context.Context, r templateRenderer, w io.Writer, tmpl string) error { +func renderUsingTemplate(ctx context.Context, r templateRenderer, w io.Writer, headerTmpl, tmpl string) error { tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0) - t, err := template.New("command").Funcs(template.FuncMap{ + base := template.New("command").Funcs(template.FuncMap{ // we render colored output if stdout is TTY, otherwise we render text. // in the future we'll check if we can explicitly check for stderr being // a TTY @@ -315,7 +322,20 @@ func renderUsingTemplate(ctx context.Context, r templateRenderer, w io.Writer, t } return string(out), nil }, - }).Parse(tmpl) + }) + if headerTmpl != "" { + headerT, err := base.Parse(headerTmpl) + if err != nil { + return err + } + err = headerT.Execute(tw, nil) + if err != nil { + return err + } + tw.Write([]byte("\n")) + // Do not flush here. Instead, allow the first 100 resources to determine the initial spacing of the header columns. + } + t, err := base.Parse(tmpl) if err != nil { return err } diff --git a/libs/cmdio/render_test.go b/libs/cmdio/render_test.go new file mode 100644 index 00000000000..e808c383cf7 --- /dev/null +++ b/libs/cmdio/render_test.go @@ -0,0 +1,133 @@ +package cmdio + +import ( + "bytes" + "context" + "strings" + "testing" + + "github.com/databricks/cli/libs/flags" + "github.com/databricks/databricks-sdk-go/listing" + "github.com/databricks/databricks-sdk-go/service/provisioning" + "github.com/stretchr/testify/assert" +) + +type testCase struct { + name string + v any + outputFormat flags.Output + headerTemplate string + template string + expected string + errMessage string +} + +var dummyWorkspace1 = provisioning.Workspace{ + WorkspaceId: 123, + WorkspaceName: "abc", +} + +var dummyWorkspace2 = provisioning.Workspace{ + WorkspaceId: 456, + WorkspaceName: "def", +} + +func makeIterator() listing.Iterator[any] { + return &dummyIterator{ + items: []any{dummyWorkspace1, dummyWorkspace2}, + } +} + +var testCases = []testCase{ + { + name: "Workspace with header and template", + v: dummyWorkspace1, + outputFormat: flags.OutputText, + headerTemplate: "id\tname", + template: "{{.WorkspaceId}}\t{{.WorkspaceName}}", + expected: `id name +123 abc`, + }, + { + name: "Workspace with no header and template", + v: dummyWorkspace1, + outputFormat: flags.OutputText, + template: "{{.WorkspaceId}}\t{{.WorkspaceName}}", + expected: `123 abc`, + }, + { + name: "Workspace with no header and no template", + v: dummyWorkspace1, + outputFormat: flags.OutputText, + expected: `{ + "workspace_id":123, + "workspace_name":"abc" +} +`, + }, + { + name: "Workspace Iterator with header and template", + v: makeIterator(), + outputFormat: flags.OutputText, + headerTemplate: "id\tname", + template: "{{range .}}{{.WorkspaceId}}\t{{.WorkspaceName}}\n{{end}}", + expected: `id name +123 abc +456 def +`, + }, + { + name: "Workspace Iterator with no header and template", + v: makeIterator(), + outputFormat: flags.OutputText, + template: "{{range .}}{{.WorkspaceId}}\t{{.WorkspaceName}}\n{{end}}", + expected: `123 abc +456 def +`, + }, + { + name: "Workspace Iterator with no header and no template", + v: makeIterator(), + outputFormat: flags.OutputText, + expected: `[ + { + "workspace_id": 123, + "workspace_name": "abc" + }, + { + "workspace_id": 456, + "workspace_name": "def" + } +] +`, + }, + { + name: "io.Reader", + v: strings.NewReader("a test"), + outputFormat: flags.OutputText, + expected: "a test", + }, + { + name: "io.Reader", + v: strings.NewReader("a test"), + outputFormat: flags.OutputJSON, + errMessage: "json output not supported", + }, +} + +func TestRender(t *testing.T) { + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + output := &bytes.Buffer{} + cmdIO := NewIO(c.outputFormat, nil, output, output, c.headerTemplate, c.template) + ctx := InContext(context.Background(), cmdIO) + err := Render(ctx, c.v) + if c.errMessage != "" { + assert.ErrorContains(t, err, c.errMessage) + } else { + assert.NoError(t, err) + assert.Equal(t, c.expected, output.String()) + } + }) + } +} diff --git a/libs/databrickscfg/cfgpickers/clusters_test.go b/libs/databrickscfg/cfgpickers/clusters_test.go index 8afcd6d0748..2e62f93a8fb 100644 --- a/libs/databrickscfg/cfgpickers/clusters_test.go +++ b/libs/databrickscfg/cfgpickers/clusters_test.go @@ -115,7 +115,7 @@ func TestFirstCompatibleCluster(t *testing.T) { w := databricks.Must(databricks.NewWorkspaceClient((*databricks.Config)(cfg))) ctx := context.Background() - ctx = cmdio.InContext(ctx, cmdio.NewIO(flags.OutputText, &bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{}, "...")) + ctx = cmdio.InContext(ctx, cmdio.NewIO(flags.OutputText, &bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{}, "", "...")) clusterID, err := AskForCluster(ctx, w, WithDatabricksConnect("13.1")) require.NoError(t, err) require.Equal(t, "bcd-id", clusterID) @@ -162,7 +162,7 @@ func TestNoCompatibleClusters(t *testing.T) { w := databricks.Must(databricks.NewWorkspaceClient((*databricks.Config)(cfg))) ctx := context.Background() - ctx = cmdio.InContext(ctx, cmdio.NewIO(flags.OutputText, &bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{}, "...")) + ctx = cmdio.InContext(ctx, cmdio.NewIO(flags.OutputText, &bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{}, "", "...")) _, err := AskForCluster(ctx, w, WithDatabricksConnect("13.1")) require.Equal(t, ErrNoCompatibleClusters, err) } diff --git a/libs/template/helpers_test.go b/libs/template/helpers_test.go index d495ae89553..a07b26f8179 100644 --- a/libs/template/helpers_test.go +++ b/libs/template/helpers_test.go @@ -111,7 +111,7 @@ func TestWorkspaceHost(t *testing.T) { func TestWorkspaceHostNotConfigured(t *testing.T) { ctx := context.Background() - cmd := cmdio.NewIO(flags.OutputJSON, strings.NewReader(""), os.Stdout, os.Stderr, "template") + cmd := cmdio.NewIO(flags.OutputJSON, strings.NewReader(""), os.Stdout, os.Stderr, "", "template") ctx = cmdio.InContext(ctx, cmd) tmpDir := t.TempDir() From bca69237ecf3e65c51af23e7a5afe49310469acc Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Mon, 12 Feb 2024 15:27:00 -0800 Subject: [PATCH 04/13] cover flush --- libs/cmdio/render_test.go | 68 ++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/libs/cmdio/render_test.go b/libs/cmdio/render_test.go index e808c383cf7..3f5ba0b35ef 100644 --- a/libs/cmdio/render_test.go +++ b/libs/cmdio/render_test.go @@ -3,6 +3,8 @@ package cmdio import ( "bytes" "context" + "encoding/json" + "fmt" "strings" "testing" @@ -32,10 +34,40 @@ var dummyWorkspace2 = provisioning.Workspace{ WorkspaceName: "def", } -func makeIterator() listing.Iterator[any] { +func makeWorkspaces(count int) []provisioning.Workspace { + res := make([]provisioning.Workspace, 0, count) + next := []provisioning.Workspace{dummyWorkspace1, dummyWorkspace2} + for i := 0; i < count; i++ { + n := next[0] + next = append(next[1:], n) + res = append(res, n) + } + return res +} + +func makeIterator(count int) listing.Iterator[any] { + items := make([]any, 0, count) + for _, ws := range makeWorkspaces(count) { + items = append(items, any(ws)) + } return &dummyIterator{ - items: []any{dummyWorkspace1, dummyWorkspace2}, + items: items, + } +} + +func makeBigOutput(count int) string { + res := bytes.Buffer{} + for _, ws := range makeWorkspaces(count) { + res.Write([]byte(fmt.Sprintf("%d %s\n", ws.WorkspaceId, ws.WorkspaceName))) + } + return res.String() +} + +func must[T any](a T, e error) T { + if e != nil { + panic(e) } + return a } var testCases = []testCase{ @@ -67,7 +99,7 @@ var testCases = []testCase{ }, { name: "Workspace Iterator with header and template", - v: makeIterator(), + v: makeIterator(2), outputFormat: flags.OutputText, headerTemplate: "id\tname", template: "{{range .}}{{.WorkspaceId}}\t{{.WorkspaceName}}\n{{end}}", @@ -78,7 +110,7 @@ var testCases = []testCase{ }, { name: "Workspace Iterator with no header and template", - v: makeIterator(), + v: makeIterator(2), outputFormat: flags.OutputText, template: "{{range .}}{{.WorkspaceId}}\t{{.WorkspaceName}}\n{{end}}", expected: `123 abc @@ -87,19 +119,23 @@ var testCases = []testCase{ }, { name: "Workspace Iterator with no header and no template", - v: makeIterator(), + v: makeIterator(2), outputFormat: flags.OutputText, - expected: `[ - { - "workspace_id": 123, - "workspace_name": "abc" - }, - { - "workspace_id": 456, - "workspace_name": "def" - } -] -`, + expected: string(must(json.MarshalIndent(makeWorkspaces(2), "", " "))) + "\n", + }, + { + name: "Big Workspace Iterator with template", + v: makeIterator(200), + outputFormat: flags.OutputText, + headerTemplate: "id\tname", + template: "{{range .}}{{.WorkspaceId}}\t{{.WorkspaceName}}\n{{end}}", + expected: "id name\n" + makeBigOutput(200), + }, + { + name: "Big Workspace Iterator with no template", + v: makeIterator(200), + outputFormat: flags.OutputText, + expected: string(must(json.MarshalIndent(makeWorkspaces(200), "", " "))) + "\n", }, { name: "io.Reader", From 503bcedf262bf1d23bb6bf1f3198af2654848557 Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Mon, 12 Feb 2024 15:42:29 -0800 Subject: [PATCH 05/13] better batching for json --- libs/cmdio/render.go | 19 ++++++++++++++----- libs/cmdio/render_test.go | 8 ++++---- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/libs/cmdio/render.go b/libs/cmdio/render.go index 235a24acfbd..5afc8cbee4a 100644 --- a/libs/cmdio/render.go +++ b/libs/cmdio/render.go @@ -136,20 +136,29 @@ func (ir iteratorRenderer) renderJson(ctx context.Context, w writeFlusher) error } func (ir iteratorRenderer) renderTemplate(ctx context.Context, t *template.Template, w writeFlusher) error { + buf := make([]any, 0, ir.getBufferSize()) for i := 0; ir.t.HasNext(ctx); i++ { n, err := ir.t.Next(ctx) if err != nil { return err } - err = t.Execute(w, []any{n}) - if err != nil { - return err - } - if (i+1)%ir.getBufferSize() == 0 { + buf = append(buf, n) + if len(buf) == cap(buf) { + err = t.Execute(w, buf) + if err != nil { + return err + } err = w.Flush() if err != nil { return err } + buf = make([]any, 0, ir.getBufferSize()) + } + } + if len(buf) > 0 { + err := t.Execute(w, buf) + if err != nil { + return err } } return w.Flush() diff --git a/libs/cmdio/render_test.go b/libs/cmdio/render_test.go index 3f5ba0b35ef..aeca075426e 100644 --- a/libs/cmdio/render_test.go +++ b/libs/cmdio/render_test.go @@ -125,17 +125,17 @@ var testCases = []testCase{ }, { name: "Big Workspace Iterator with template", - v: makeIterator(200), + v: makeIterator(234), outputFormat: flags.OutputText, headerTemplate: "id\tname", template: "{{range .}}{{.WorkspaceId}}\t{{.WorkspaceName}}\n{{end}}", - expected: "id name\n" + makeBigOutput(200), + expected: "id name\n" + makeBigOutput(234), }, { name: "Big Workspace Iterator with no template", - v: makeIterator(200), + v: makeIterator(234), outputFormat: flags.OutputText, - expected: string(must(json.MarshalIndent(makeWorkspaces(200), "", " "))) + "\n", + expected: string(must(json.MarshalIndent(makeWorkspaces(234), "", " "))) + "\n", }, { name: "io.Reader", From c493fa7078865d758554dc6418268e8f593eb8d4 Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Tue, 13 Feb 2024 06:03:44 -0800 Subject: [PATCH 06/13] work --- .codegen/_openapi_sha | 2 +- .codegen/service.go.tmpl | 2 +- .gitattributes | 1 + bundle/schema/docs/bundle_descriptions.json | 166 ++++++- cmd/account/budgets/budgets.go | 1 - .../custom-app-integration.go | 1 - cmd/account/groups/groups.go | 1 - .../ip-access-lists/ip-access-lists.go | 1 - cmd/account/log-delivery/log-delivery.go | 1 - .../metastore-assignments.go | 1 - cmd/account/metastores/metastores.go | 1 - .../network-connectivity.go | 2 - .../o-auth-published-apps.go | 1 - .../published-app-integration.go | 1 - .../service-principal-secrets.go | 1 - .../service-principals/service-principals.go | 1 - cmd/account/users/users.go | 1 - .../workspace-assignment.go | 1 - cmd/workspace/catalogs/catalogs.go | 1 - cmd/workspace/clean-rooms/clean-rooms.go | 1 - .../cluster-policies/cluster-policies.go | 10 +- cmd/workspace/clusters/clusters.go | 7 +- cmd/workspace/cmd.go | 2 + cmd/workspace/connections/connections.go | 1 - cmd/workspace/dashboards/dashboards.go | 96 +++- cmd/workspace/experiments/experiments.go | 13 +- .../external-locations/external-locations.go | 1 - cmd/workspace/functions/functions.go | 1 - .../git-credentials/git-credentials.go | 1 - .../global-init-scripts.go | 3 +- cmd/workspace/groups/groups.go | 1 - .../instance-pools/instance-pools.go | 1 - .../instance-profiles/instance-profiles.go | 1 - .../ip-access-lists/ip-access-lists.go | 1 - cmd/workspace/jobs/jobs.go | 2 - .../lakehouse-monitors/lakehouse-monitors.go | 414 ++++++++++++++++++ cmd/workspace/libraries/libraries.go | 1 - cmd/workspace/metastores/metastores.go | 1 - .../model-registry/model-registry.go | 6 - .../model-versions/model-versions.go | 1 - cmd/workspace/pipelines/pipelines.go | 3 +- .../policy-families/policy-families.go | 1 - cmd/workspace/providers/providers.go | 2 - cmd/workspace/queries/queries.go | 7 +- cmd/workspace/query-history/query-history.go | 1 - cmd/workspace/recipients/recipients.go | 1 - .../registered-models/registered-models.go | 1 - cmd/workspace/repos/repos.go | 1 - cmd/workspace/schemas/schemas.go | 1 - cmd/workspace/secrets/secrets.go | 3 - .../service-principals/service-principals.go | 1 - .../serving-endpoints/serving-endpoints.go | 1 - cmd/workspace/shares/shares.go | 1 - .../storage-credentials.go | 1 - .../system-schemas/system-schemas.go | 1 - cmd/workspace/tables/tables.go | 95 +++- .../token-management/token-management.go | 54 ++- cmd/workspace/tokens/tokens.go | 1 - cmd/workspace/users/users.go | 1 - .../vector-search-endpoints.go | 1 - .../vector-search-indexes.go | 1 - cmd/workspace/volumes/volumes.go | 1 - cmd/workspace/warehouses/warehouses.go | 1 - cmd/workspace/workspace/workspace.go | 1 - libs/cmdio/reflect_iterator.go | 4 - libs/cmdio/render.go | 8 +- 66 files changed, 815 insertions(+), 131 deletions(-) create mode 100755 cmd/workspace/lakehouse-monitors/lakehouse-monitors.go diff --git a/.codegen/_openapi_sha b/.codegen/_openapi_sha index 56c8253ff88..f705ffea629 100644 --- a/.codegen/_openapi_sha +++ b/.codegen/_openapi_sha @@ -1 +1 @@ -a7a9dc025bb80303e676bf3708942c6aa06689f1 \ No newline at end of file +e05401ed5dd4974c5333d737ec308a7d451f749f \ No newline at end of file diff --git a/.codegen/service.go.tmpl b/.codegen/service.go.tmpl index 0eb1192e7e8..f1d1ca5aca2 100644 --- a/.codegen/service.go.tmpl +++ b/.codegen/service.go.tmpl @@ -305,7 +305,7 @@ func init() { {{- else -}} err = {{- end}} {{if .Service.IsAccounts}}a{{else}}w{{end}}.{{(.Service.TrimPrefix "account").PascalName}}.{{.PascalName}}(ctx{{if .Request}}, {{.CamelName}}Req{{end}}) - {{if not (and .Response .Pagination) -}} + {{- if not (and .Response .Pagination) }} if err != nil { return err } diff --git a/.gitattributes b/.gitattributes index 7a1750caa83..fe33227a7e7 100755 --- a/.gitattributes +++ b/.gitattributes @@ -50,6 +50,7 @@ cmd/workspace/instance-pools/instance-pools.go linguist-generated=true cmd/workspace/instance-profiles/instance-profiles.go linguist-generated=true cmd/workspace/ip-access-lists/ip-access-lists.go linguist-generated=true cmd/workspace/jobs/jobs.go linguist-generated=true +cmd/workspace/lakehouse-monitors/lakehouse-monitors.go linguist-generated=true cmd/workspace/lakeview/lakeview.go linguist-generated=true cmd/workspace/libraries/libraries.go linguist-generated=true cmd/workspace/metastores/metastores.go linguist-generated=true diff --git a/bundle/schema/docs/bundle_descriptions.json b/bundle/schema/docs/bundle_descriptions.json index fd25f4300cf..2308846401b 100644 --- a/bundle/schema/docs/bundle_descriptions.json +++ b/bundle/schema/docs/bundle_descriptions.json @@ -9,6 +9,9 @@ "build": { "description": "" }, + "executable": { + "description": "" + }, "files": { "description": "", "items": { @@ -35,6 +38,14 @@ "compute_id": { "description": "" }, + "deployment": { + "description": "", + "properties": { + "fail_on_active_runs": { + "description": "" + } + } + }, "git": { "description": "", "properties": { @@ -803,7 +814,7 @@ } }, "existing_cluster_id": { - "description": "If existing_cluster_id, the ID of an existing cluster that is used for all runs of this task. When running tasks on an existing cluster, you may need to manually restart the cluster if it stops responding. We suggest running jobs on new clusters for greater reliability." + "description": "If existing_cluster_id, the ID of an existing cluster that is used for all runs of this task. Only all-purpose clusters are supported. When running tasks on an existing cluster, you may need to manually restart the cluster if it stops responding. We suggest running jobs on new clusters for greater reliability." }, "health": { "description": "", @@ -1210,7 +1221,7 @@ "description": "The path of the notebook to be run in the Databricks workspace or remote repository.\nFor notebooks stored in the Databricks workspace, the path must be absolute and begin with a slash.\nFor notebooks stored in a remote repository, the path must be relative. This field is required.\n" }, "source": { - "description": "Optional location type of the notebook. When set to `WORKSPACE`, the notebook will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the notebook will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Notebook is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Notebook is located in cloud Git provider.\n" + "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" } } }, @@ -1312,7 +1323,7 @@ "description": "The Python file to be executed. Cloud file URIs (such as dbfs:/, s3:/, adls:/, gcs:/) and workspace paths are supported. For python files stored in the Databricks workspace, the path must be absolute and begin with `/`. For files stored in a remote repository, the path must be relative. This field is required." }, "source": { - "description": "Optional location type of the notebook. When set to `WORKSPACE`, the notebook will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the notebook will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Notebook is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Notebook is located in cloud Git provider.\n" + "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" } } }, @@ -2093,6 +2104,72 @@ } } }, + "init_scripts": { + "description": "The configuration for storing init scripts. Any number of destinations can be specified. The scripts are executed sequentially in the order provided. If `cluster_log_conf` is specified, init script logs are sent to `\u003cdestination\u003e/\u003ccluster-ID\u003e/init_scripts`.", + "items": { + "description": "", + "properties": { + "dbfs": { + "description": "destination needs to be provided. e.g.\n`{ \"dbfs\" : { \"destination\" : \"dbfs:/home/cluster_log\" } }`", + "properties": { + "destination": { + "description": "dbfs destination, e.g. `dbfs:/my/path`" + } + } + }, + "file": { + "description": "destination needs to be provided. e.g.\n`{ \"file\" : { \"destination\" : \"file:/my/local/file.sh\" } }`", + "properties": { + "destination": { + "description": "local file destination, e.g. `file:/my/local/file.sh`" + } + } + }, + "s3": { + "description": "destination and either the region or endpoint need to be provided. e.g.\n`{ \"s3\": { \"destination\" : \"s3://cluster_log_bucket/prefix\", \"region\" : \"us-west-2\" } }`\nCluster iam role is used to access s3, please make sure the cluster iam role in\n`instance_profile_arn` has permission to write data to the s3 destination.", + "properties": { + "canned_acl": { + "description": "(Optional) Set canned access control list for the logs, e.g. `bucket-owner-full-control`.\nIf `canned_cal` is set, please make sure the cluster iam role has `s3:PutObjectAcl` permission on\nthe destination bucket and prefix. The full list of possible canned acl can be found at\nhttp://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl.\nPlease also note that by default only the object owner gets full controls. If you are using cross account\nrole for writing data, you may want to set `bucket-owner-full-control` to make bucket owner able to\nread the logs." + }, + "destination": { + "description": "S3 destination, e.g. `s3://my-bucket/some-prefix` Note that logs will be delivered using\ncluster iam role, please make sure you set cluster iam role and the role has write access to the\ndestination. Please also note that you cannot use AWS keys to deliver logs." + }, + "enable_encryption": { + "description": "(Optional) Flag to enable server side encryption, `false` by default." + }, + "encryption_type": { + "description": "(Optional) The encryption type, it could be `sse-s3` or `sse-kms`. It will be used only when\nencryption is enabled and the default type is `sse-s3`." + }, + "endpoint": { + "description": "S3 endpoint, e.g. `https://s3-us-west-2.amazonaws.com`. Either region or endpoint needs to be set.\nIf both are set, endpoint will be used." + }, + "kms_key": { + "description": "(Optional) Kms key which will be used if encryption is enabled and encryption type is set to `sse-kms`." + }, + "region": { + "description": "S3 region, e.g. `us-west-2`. Either region or endpoint needs to be set. If both are set,\nendpoint will be used." + } + } + }, + "volumes": { + "description": "destination needs to be provided. e.g.\n`{ \"volumes\" : { \"destination\" : \"/Volumes/my-init.sh\" } }`", + "properties": { + "destination": { + "description": "Unity Catalog Volumes file destination, e.g. `/Volumes/my-init.sh`" + } + } + }, + "workspace": { + "description": "destination needs to be provided. e.g.\n`{ \"workspace\" : { \"destination\" : \"/Users/user1@databricks.com/my-init.sh\" } }`", + "properties": { + "destination": { + "description": "workspace files destination, e.g. `/Users/user1@databricks.com/my-init.sh`" + } + } + } + } + } + }, "instance_pool_id": { "description": "The optional ID of the instance pool to which the cluster belongs." }, @@ -2368,6 +2445,9 @@ "build": { "description": "" }, + "executable": { + "description": "" + }, "files": { "description": "", "items": { @@ -2394,6 +2474,14 @@ "compute_id": { "description": "" }, + "deployment": { + "description": "", + "properties": { + "fail_on_active_runs": { + "description": "" + } + } + }, "git": { "description": "", "properties": { @@ -3162,7 +3250,7 @@ } }, "existing_cluster_id": { - "description": "If existing_cluster_id, the ID of an existing cluster that is used for all runs of this task. When running tasks on an existing cluster, you may need to manually restart the cluster if it stops responding. We suggest running jobs on new clusters for greater reliability." + "description": "If existing_cluster_id, the ID of an existing cluster that is used for all runs of this task. Only all-purpose clusters are supported. When running tasks on an existing cluster, you may need to manually restart the cluster if it stops responding. We suggest running jobs on new clusters for greater reliability." }, "health": { "description": "", @@ -3569,7 +3657,7 @@ "description": "The path of the notebook to be run in the Databricks workspace or remote repository.\nFor notebooks stored in the Databricks workspace, the path must be absolute and begin with a slash.\nFor notebooks stored in a remote repository, the path must be relative. This field is required.\n" }, "source": { - "description": "Optional location type of the notebook. When set to `WORKSPACE`, the notebook will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the notebook will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Notebook is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Notebook is located in cloud Git provider.\n" + "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" } } }, @@ -3671,7 +3759,7 @@ "description": "The Python file to be executed. Cloud file URIs (such as dbfs:/, s3:/, adls:/, gcs:/) and workspace paths are supported. For python files stored in the Databricks workspace, the path must be absolute and begin with `/`. For files stored in a remote repository, the path must be relative. This field is required." }, "source": { - "description": "Optional location type of the notebook. When set to `WORKSPACE`, the notebook will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the notebook will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Notebook is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Notebook is located in cloud Git provider.\n" + "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" } } }, @@ -4452,6 +4540,72 @@ } } }, + "init_scripts": { + "description": "The configuration for storing init scripts. Any number of destinations can be specified. The scripts are executed sequentially in the order provided. If `cluster_log_conf` is specified, init script logs are sent to `\u003cdestination\u003e/\u003ccluster-ID\u003e/init_scripts`.", + "items": { + "description": "", + "properties": { + "dbfs": { + "description": "destination needs to be provided. e.g.\n`{ \"dbfs\" : { \"destination\" : \"dbfs:/home/cluster_log\" } }`", + "properties": { + "destination": { + "description": "dbfs destination, e.g. `dbfs:/my/path`" + } + } + }, + "file": { + "description": "destination needs to be provided. e.g.\n`{ \"file\" : { \"destination\" : \"file:/my/local/file.sh\" } }`", + "properties": { + "destination": { + "description": "local file destination, e.g. `file:/my/local/file.sh`" + } + } + }, + "s3": { + "description": "destination and either the region or endpoint need to be provided. e.g.\n`{ \"s3\": { \"destination\" : \"s3://cluster_log_bucket/prefix\", \"region\" : \"us-west-2\" } }`\nCluster iam role is used to access s3, please make sure the cluster iam role in\n`instance_profile_arn` has permission to write data to the s3 destination.", + "properties": { + "canned_acl": { + "description": "(Optional) Set canned access control list for the logs, e.g. `bucket-owner-full-control`.\nIf `canned_cal` is set, please make sure the cluster iam role has `s3:PutObjectAcl` permission on\nthe destination bucket and prefix. The full list of possible canned acl can be found at\nhttp://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl.\nPlease also note that by default only the object owner gets full controls. If you are using cross account\nrole for writing data, you may want to set `bucket-owner-full-control` to make bucket owner able to\nread the logs." + }, + "destination": { + "description": "S3 destination, e.g. `s3://my-bucket/some-prefix` Note that logs will be delivered using\ncluster iam role, please make sure you set cluster iam role and the role has write access to the\ndestination. Please also note that you cannot use AWS keys to deliver logs." + }, + "enable_encryption": { + "description": "(Optional) Flag to enable server side encryption, `false` by default." + }, + "encryption_type": { + "description": "(Optional) The encryption type, it could be `sse-s3` or `sse-kms`. It will be used only when\nencryption is enabled and the default type is `sse-s3`." + }, + "endpoint": { + "description": "S3 endpoint, e.g. `https://s3-us-west-2.amazonaws.com`. Either region or endpoint needs to be set.\nIf both are set, endpoint will be used." + }, + "kms_key": { + "description": "(Optional) Kms key which will be used if encryption is enabled and encryption type is set to `sse-kms`." + }, + "region": { + "description": "S3 region, e.g. `us-west-2`. Either region or endpoint needs to be set. If both are set,\nendpoint will be used." + } + } + }, + "volumes": { + "description": "destination needs to be provided. e.g.\n`{ \"volumes\" : { \"destination\" : \"/Volumes/my-init.sh\" } }`", + "properties": { + "destination": { + "description": "Unity Catalog Volumes file destination, e.g. `/Volumes/my-init.sh`" + } + } + }, + "workspace": { + "description": "destination needs to be provided. e.g.\n`{ \"workspace\" : { \"destination\" : \"/Users/user1@databricks.com/my-init.sh\" } }`", + "properties": { + "destination": { + "description": "workspace files destination, e.g. `/Users/user1@databricks.com/my-init.sh`" + } + } + } + } + } + }, "instance_pool_id": { "description": "The optional ID of the instance pool to which the cluster belongs." }, diff --git a/cmd/account/budgets/budgets.go b/cmd/account/budgets/budgets.go index 43fcb4c976d..f0544abc5c4 100755 --- a/cmd/account/budgets/budgets.go +++ b/cmd/account/budgets/budgets.go @@ -282,7 +282,6 @@ func newList() *cobra.Command { ctx := cmd.Context() a := root.AccountClient(ctx) response := a.Budgets.List(ctx) - return cmdio.Render(ctx, response) } diff --git a/cmd/account/custom-app-integration/custom-app-integration.go b/cmd/account/custom-app-integration/custom-app-integration.go index a45935ca89b..3152e28fd56 100755 --- a/cmd/account/custom-app-integration/custom-app-integration.go +++ b/cmd/account/custom-app-integration/custom-app-integration.go @@ -263,7 +263,6 @@ func newList() *cobra.Command { ctx := cmd.Context() a := root.AccountClient(ctx) response := a.CustomAppIntegration.List(ctx) - return cmdio.Render(ctx, response) } diff --git a/cmd/account/groups/groups.go b/cmd/account/groups/groups.go index 528b4b0e132..7c344a99aa7 100755 --- a/cmd/account/groups/groups.go +++ b/cmd/account/groups/groups.go @@ -315,7 +315,6 @@ func newList() *cobra.Command { a := root.AccountClient(ctx) response := a.Groups.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/account/ip-access-lists/ip-access-lists.go b/cmd/account/ip-access-lists/ip-access-lists.go index b3d3b723fcf..85d8369d4e0 100755 --- a/cmd/account/ip-access-lists/ip-access-lists.go +++ b/cmd/account/ip-access-lists/ip-access-lists.go @@ -340,7 +340,6 @@ func newList() *cobra.Command { ctx := cmd.Context() a := root.AccountClient(ctx) response := a.IpAccessLists.List(ctx) - return cmdio.Render(ctx, response) } diff --git a/cmd/account/log-delivery/log-delivery.go b/cmd/account/log-delivery/log-delivery.go index 9c5df16f871..fc60864333b 100755 --- a/cmd/account/log-delivery/log-delivery.go +++ b/cmd/account/log-delivery/log-delivery.go @@ -304,7 +304,6 @@ func newList() *cobra.Command { a := root.AccountClient(ctx) response := a.LogDelivery.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/account/metastore-assignments/metastore-assignments.go b/cmd/account/metastore-assignments/metastore-assignments.go index 0d1d1ef3030..ec4a747f588 100755 --- a/cmd/account/metastore-assignments/metastore-assignments.go +++ b/cmd/account/metastore-assignments/metastore-assignments.go @@ -295,7 +295,6 @@ func newList() *cobra.Command { listReq.MetastoreId = args[0] response := a.MetastoreAssignments.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/account/metastores/metastores.go b/cmd/account/metastores/metastores.go index 67d7e4c7739..fa95e5f2c56 100755 --- a/cmd/account/metastores/metastores.go +++ b/cmd/account/metastores/metastores.go @@ -258,7 +258,6 @@ func newList() *cobra.Command { ctx := cmd.Context() a := root.AccountClient(ctx) response := a.Metastores.List(ctx) - return cmdio.Render(ctx, response) } diff --git a/cmd/account/network-connectivity/network-connectivity.go b/cmd/account/network-connectivity/network-connectivity.go index db6c05c6ecd..dad41fe256b 100755 --- a/cmd/account/network-connectivity/network-connectivity.go +++ b/cmd/account/network-connectivity/network-connectivity.go @@ -547,7 +547,6 @@ func newListNetworkConnectivityConfigurations() *cobra.Command { a := root.AccountClient(ctx) response := a.NetworkConnectivity.ListNetworkConnectivityConfigurations(ctx, listNetworkConnectivityConfigurationsReq) - return cmdio.Render(ctx, response) } @@ -611,7 +610,6 @@ func newListPrivateEndpointRules() *cobra.Command { listPrivateEndpointRulesReq.NetworkConnectivityConfigId = args[0] response := a.NetworkConnectivity.ListPrivateEndpointRules(ctx, listPrivateEndpointRulesReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/account/o-auth-published-apps/o-auth-published-apps.go b/cmd/account/o-auth-published-apps/o-auth-published-apps.go index 343c052c971..5a0b7e31cad 100755 --- a/cmd/account/o-auth-published-apps/o-auth-published-apps.go +++ b/cmd/account/o-auth-published-apps/o-auth-published-apps.go @@ -73,7 +73,6 @@ func newList() *cobra.Command { a := root.AccountClient(ctx) response := a.OAuthPublishedApps.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/account/published-app-integration/published-app-integration.go b/cmd/account/published-app-integration/published-app-integration.go index 04ec452c678..b38395c1048 100755 --- a/cmd/account/published-app-integration/published-app-integration.go +++ b/cmd/account/published-app-integration/published-app-integration.go @@ -263,7 +263,6 @@ func newList() *cobra.Command { ctx := cmd.Context() a := root.AccountClient(ctx) response := a.PublishedAppIntegration.List(ctx) - return cmdio.Render(ctx, response) } diff --git a/cmd/account/service-principal-secrets/service-principal-secrets.go b/cmd/account/service-principal-secrets/service-principal-secrets.go index d30764af7a2..eb7ef60651c 100755 --- a/cmd/account/service-principal-secrets/service-principal-secrets.go +++ b/cmd/account/service-principal-secrets/service-principal-secrets.go @@ -227,7 +227,6 @@ func newList() *cobra.Command { } response := a.ServicePrincipalSecrets.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/account/service-principals/service-principals.go b/cmd/account/service-principals/service-principals.go index 7bff26cfd65..709f2647c64 100755 --- a/cmd/account/service-principals/service-principals.go +++ b/cmd/account/service-principals/service-principals.go @@ -314,7 +314,6 @@ func newList() *cobra.Command { a := root.AccountClient(ctx) response := a.ServicePrincipals.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/account/users/users.go b/cmd/account/users/users.go index d277fb1f174..e2d3555cc09 100755 --- a/cmd/account/users/users.go +++ b/cmd/account/users/users.go @@ -330,7 +330,6 @@ func newList() *cobra.Command { a := root.AccountClient(ctx) response := a.Users.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/account/workspace-assignment/workspace-assignment.go b/cmd/account/workspace-assignment/workspace-assignment.go index d6880a37a5b..396c5290dda 100755 --- a/cmd/account/workspace-assignment/workspace-assignment.go +++ b/cmd/account/workspace-assignment/workspace-assignment.go @@ -220,7 +220,6 @@ func newList() *cobra.Command { } response := a.WorkspaceAssignment.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/catalogs/catalogs.go b/cmd/workspace/catalogs/catalogs.go index a66c3d80911..f44bdc18d93 100755 --- a/cmd/workspace/catalogs/catalogs.go +++ b/cmd/workspace/catalogs/catalogs.go @@ -293,7 +293,6 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.Catalogs.List(ctx) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/clean-rooms/clean-rooms.go b/cmd/workspace/clean-rooms/clean-rooms.go index b100c07dfb8..50477724bca 100755 --- a/cmd/workspace/clean-rooms/clean-rooms.go +++ b/cmd/workspace/clean-rooms/clean-rooms.go @@ -283,7 +283,6 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.CleanRooms.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/cluster-policies/cluster-policies.go b/cmd/workspace/cluster-policies/cluster-policies.go index 39e4a15364d..bfe50523655 100755 --- a/cmd/workspace/cluster-policies/cluster-policies.go +++ b/cmd/workspace/cluster-policies/cluster-policies.go @@ -26,10 +26,11 @@ func New() *cobra.Command { limit their use to specific users and groups. With cluster policies, you can: - Auto-install cluster libraries on the next - restart by listing them in the policy's "libraries" field. - Limit users to - creating clusters with the prescribed settings. - Simplify the user interface, - enabling more users to create clusters, by fixing and hiding some fields. - - Manage costs by setting limits on attributes that impact the hourly rate. + restart by listing them in the policy's "libraries" field (Public Preview). - + Limit users to creating clusters with the prescribed settings. - Simplify the + user interface, enabling more users to create clusters, by fixing and hiding + some fields. - Manage costs by setting limits on attributes that impact the + hourly rate. Cluster policy permissions limit which policies a user can select in the Policy drop-down when the user creates a cluster: - A user who has @@ -603,7 +604,6 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.ClusterPolicies.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/clusters/clusters.go b/cmd/workspace/clusters/clusters.go index 3dc9221f448..9f49beb5990 100755 --- a/cmd/workspace/clusters/clusters.go +++ b/cmd/workspace/clusters/clusters.go @@ -84,8 +84,9 @@ func newChangeOwner() *cobra.Command { cmd.Short = `Change cluster owner.` cmd.Long = `Change cluster owner. - Change the owner of the cluster. You must be an admin to perform this - operation. + Change the owner of the cluster. You must be an admin and the cluster must be + terminated to perform this operation. The service principal application ID can + be supplied as an argument to owner_username. Arguments: CLUSTER_ID: @@ -653,7 +654,6 @@ func newEvents() *cobra.Command { } response := w.Clusters.Events(ctx, eventsReq) - return cmdio.Render(ctx, response) } @@ -955,7 +955,6 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Clusters.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/cmd.go b/cmd/workspace/cmd.go index f8e911d1f14..47ad795e634 100755 --- a/cmd/workspace/cmd.go +++ b/cmd/workspace/cmd.go @@ -27,6 +27,7 @@ import ( instance_profiles "github.com/databricks/cli/cmd/workspace/instance-profiles" ip_access_lists "github.com/databricks/cli/cmd/workspace/ip-access-lists" jobs "github.com/databricks/cli/cmd/workspace/jobs" + lakehouse_monitors "github.com/databricks/cli/cmd/workspace/lakehouse-monitors" lakeview "github.com/databricks/cli/cmd/workspace/lakeview" libraries "github.com/databricks/cli/cmd/workspace/libraries" metastores "github.com/databricks/cli/cmd/workspace/metastores" @@ -93,6 +94,7 @@ func All() []*cobra.Command { out = append(out, instance_profiles.New()) out = append(out, ip_access_lists.New()) out = append(out, jobs.New()) + out = append(out, lakehouse_monitors.New()) out = append(out, lakeview.New()) out = append(out, libraries.New()) out = append(out, metastores.New()) diff --git a/cmd/workspace/connections/connections.go b/cmd/workspace/connections/connections.go index 08bbaa2adc0..52f93391a1c 100755 --- a/cmd/workspace/connections/connections.go +++ b/cmd/workspace/connections/connections.go @@ -294,7 +294,6 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.Connections.List(ctx) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/dashboards/dashboards.go b/cmd/workspace/dashboards/dashboards.go index f5e12187af2..3b21a874836 100755 --- a/cmd/workspace/dashboards/dashboards.go +++ b/cmd/workspace/dashboards/dashboards.go @@ -122,7 +122,7 @@ func newDelete() *cobra.Command { cmd.Use = "delete DASHBOARD_ID" cmd.Short = `Remove a dashboard.` cmd.Long = `Remove a dashboard. - + Moves a dashboard to the trash. Trashed dashboards do not appear in list views or searches, and cannot be shared.` @@ -196,7 +196,7 @@ func newGet() *cobra.Command { cmd.Use = "get DASHBOARD_ID" cmd.Short = `Retrieve a definition.` cmd.Long = `Retrieve a definition. - + Returns a JSON representation of a dashboard object, including its visualization and query objects.` @@ -275,7 +275,7 @@ func newList() *cobra.Command { cmd.Use = "list" cmd.Short = `Get dashboard objects.` cmd.Long = `Get dashboard objects. - + Fetch a paginated list of dashboard objects.` cmd.Annotations = make(map[string]string) @@ -291,7 +291,6 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Dashboards.List(ctx, listReq) - return cmdio.Render(ctx, response) } @@ -332,7 +331,7 @@ func newRestore() *cobra.Command { cmd.Use = "restore DASHBOARD_ID" cmd.Short = `Restore a dashboard.` cmd.Long = `Restore a dashboard. - + A restored dashboard appears in list views and searches and can be shared.` cmd.Annotations = make(map[string]string) @@ -386,4 +385,91 @@ func init() { }) } +// start update command + +// Slice with functions to override default command behavior. +// Functions can be added from the `init()` function in manually curated files in this directory. +var updateOverrides []func( + *cobra.Command, + *sql.DashboardEditContent, +) + +func newUpdate() *cobra.Command { + cmd := &cobra.Command{} + + var updateReq sql.DashboardEditContent + var updateJson flags.JsonFlag + + // TODO: short flags + cmd.Flags().Var(&updateJson, "json", `either inline JSON string or @path/to/file.json with request body`) + + cmd.Flags().StringVar(&updateReq.Name, "name", updateReq.Name, `The title of this dashboard that appears in list views and at the top of the dashboard page.`) + cmd.Flags().Var(&updateReq.RunAsRole, "run-as-role", `Sets the **Run as** role for the object. Supported values: [owner, viewer]`) + + cmd.Use = "update DASHBOARD_ID" + cmd.Short = `Change a dashboard definition.` + cmd.Long = `Change a dashboard definition. + + Modify this dashboard definition. This operation only affects attributes of + the dashboard object. It does not add, modify, or remove widgets. + + **Note**: You cannot undo this operation.` + + cmd.Annotations = make(map[string]string) + + cmd.PreRunE = root.MustWorkspaceClient + cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { + ctx := cmd.Context() + w := root.WorkspaceClient(ctx) + + if cmd.Flags().Changed("json") { + err = updateJson.Unmarshal(&updateReq) + if err != nil { + return err + } + } + if len(args) == 0 { + promptSpinner := cmdio.Spinner(ctx) + promptSpinner <- "No DASHBOARD_ID argument specified. Loading names for Dashboards drop-down." + names, err := w.Dashboards.DashboardNameToIdMap(ctx, sql.ListDashboardsRequest{}) + close(promptSpinner) + if err != nil { + return fmt.Errorf("failed to load names for Dashboards drop-down. Please manually specify required arguments. Original error: %w", err) + } + id, err := cmdio.Select(ctx, names, "") + if err != nil { + return err + } + args = append(args, id) + } + if len(args) != 1 { + return fmt.Errorf("expected to have ") + } + updateReq.DashboardId = args[0] + + response, err := w.Dashboards.Update(ctx, updateReq) + if err != nil { + return err + } + return cmdio.Render(ctx, response) + } + + // Disable completions since they are not applicable. + // Can be overridden by manual implementation in `override.go`. + cmd.ValidArgsFunction = cobra.NoFileCompletions + + // Apply optional overrides to this command. + for _, fn := range updateOverrides { + fn(cmd, &updateReq) + } + + return cmd +} + +func init() { + cmdOverrides = append(cmdOverrides, func(cmd *cobra.Command) { + cmd.AddCommand(newUpdate()) + }) +} + // end service Dashboards diff --git a/cmd/workspace/experiments/experiments.go b/cmd/workspace/experiments/experiments.go index c4b1d594acd..653e8fe242e 100755 --- a/cmd/workspace/experiments/experiments.go +++ b/cmd/workspace/experiments/experiments.go @@ -397,7 +397,9 @@ func newDeleteRuns() *cobra.Command { cmd.Long = `Delete runs by creation time. Bulk delete runs in an experiment that were created prior to or at the - specified timestamp. Deletes at most max_runs per request. + specified timestamp. Deletes at most max_runs per request. To call this API + from a Databricks Notebook in Python, you can use the client code snippet on + https://learn.microsoft.com/en-us/azure/databricks/mlflow/runs#bulk-delete. Arguments: EXPERIMENT_ID: The ID of the experiment containing the runs to delete. @@ -732,7 +734,6 @@ func newGetHistory() *cobra.Command { getHistoryReq.MetricKey = args[0] response := w.Experiments.GetHistory(ctx, getHistoryReq) - return cmdio.Render(ctx, response) } @@ -995,7 +996,6 @@ func newListArtifacts() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Experiments.ListArtifacts(ctx, listArtifactsReq) - return cmdio.Render(ctx, response) } @@ -1056,7 +1056,6 @@ func newListExperiments() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Experiments.ListExperiments(ctx, listExperimentsReq) - return cmdio.Render(ctx, response) } @@ -1715,7 +1714,9 @@ func newRestoreRuns() *cobra.Command { cmd.Long = `Restore runs by deletion time. Bulk restore runs in an experiment that were deleted no earlier than the - specified timestamp. Restores at most max_runs per request. + specified timestamp. Restores at most max_runs per request. To call this API + from a Databricks Notebook in Python, you can use the client code snippet on + https://learn.microsoft.com/en-us/azure/databricks/mlflow/runs#bulk-restore. Arguments: EXPERIMENT_ID: The ID of the experiment containing the runs to restore. @@ -1833,7 +1834,6 @@ func newSearchExperiments() *cobra.Command { } response := w.Experiments.SearchExperiments(ctx, searchExperimentsReq) - return cmdio.Render(ctx, response) } @@ -1908,7 +1908,6 @@ func newSearchRuns() *cobra.Command { } response := w.Experiments.SearchRuns(ctx, searchRunsReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/external-locations/external-locations.go b/cmd/workspace/external-locations/external-locations.go index aefdb0266d1..5d0b9e1c3f4 100755 --- a/cmd/workspace/external-locations/external-locations.go +++ b/cmd/workspace/external-locations/external-locations.go @@ -320,7 +320,6 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.ExternalLocations.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/functions/functions.go b/cmd/workspace/functions/functions.go index 2b84f8d3bd9..d1f7935854f 100755 --- a/cmd/workspace/functions/functions.go +++ b/cmd/workspace/functions/functions.go @@ -328,7 +328,6 @@ func newList() *cobra.Command { listReq.SchemaName = args[1] response := w.Functions.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/git-credentials/git-credentials.go b/cmd/workspace/git-credentials/git-credentials.go index 4300e30eb2a..6e563e85a86 100755 --- a/cmd/workspace/git-credentials/git-credentials.go +++ b/cmd/workspace/git-credentials/git-credentials.go @@ -312,7 +312,6 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.GitCredentials.List(ctx) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/global-init-scripts/global-init-scripts.go b/cmd/workspace/global-init-scripts/global-init-scripts.go index 1e4da9c84a4..389ef86d0a3 100755 --- a/cmd/workspace/global-init-scripts/global-init-scripts.go +++ b/cmd/workspace/global-init-scripts/global-init-scripts.go @@ -301,7 +301,7 @@ func newList() *cobra.Command { Get a list of all global init scripts for this workspace. This returns all properties for each script but **not** the script contents. To retrieve the contents of a script, use the [get a global init - script](#operation/get-script) operation.` + script](:method:globalinitscripts/get) operation.` cmd.Annotations = make(map[string]string) @@ -310,7 +310,6 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.GlobalInitScripts.List(ctx) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/groups/groups.go b/cmd/workspace/groups/groups.go index 7b98086e93b..a6a9fdf7e89 100755 --- a/cmd/workspace/groups/groups.go +++ b/cmd/workspace/groups/groups.go @@ -315,7 +315,6 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Groups.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/instance-pools/instance-pools.go b/cmd/workspace/instance-pools/instance-pools.go index d1105c32160..8529b027d6c 100755 --- a/cmd/workspace/instance-pools/instance-pools.go +++ b/cmd/workspace/instance-pools/instance-pools.go @@ -603,7 +603,6 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.InstancePools.List(ctx) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/instance-profiles/instance-profiles.go b/cmd/workspace/instance-profiles/instance-profiles.go index b4d0331c467..a6935348e23 100755 --- a/cmd/workspace/instance-profiles/instance-profiles.go +++ b/cmd/workspace/instance-profiles/instance-profiles.go @@ -252,7 +252,6 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.InstanceProfiles.List(ctx) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/ip-access-lists/ip-access-lists.go b/cmd/workspace/ip-access-lists/ip-access-lists.go index 49bb4d4a697..08a868a72b2 100755 --- a/cmd/workspace/ip-access-lists/ip-access-lists.go +++ b/cmd/workspace/ip-access-lists/ip-access-lists.go @@ -341,7 +341,6 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.IpAccessLists.List(ctx) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/jobs/jobs.go b/cmd/workspace/jobs/jobs.go index 6e48601002d..7f539a8f64b 100755 --- a/cmd/workspace/jobs/jobs.go +++ b/cmd/workspace/jobs/jobs.go @@ -1043,7 +1043,6 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Jobs.List(ctx, listReq) - return cmdio.Render(ctx, response) } @@ -1111,7 +1110,6 @@ func newListRuns() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Jobs.ListRuns(ctx, listRunsReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/lakehouse-monitors/lakehouse-monitors.go b/cmd/workspace/lakehouse-monitors/lakehouse-monitors.go new file mode 100755 index 00000000000..3a644b9330f --- /dev/null +++ b/cmd/workspace/lakehouse-monitors/lakehouse-monitors.go @@ -0,0 +1,414 @@ +// Code generated from OpenAPI specs by Databricks SDK Generator. DO NOT EDIT. + +package lakehouse_monitors + +import ( + "fmt" + + "github.com/databricks/cli/cmd/root" + "github.com/databricks/cli/libs/cmdio" + "github.com/databricks/cli/libs/flags" + "github.com/databricks/databricks-sdk-go/service/catalog" + "github.com/spf13/cobra" +) + +// Slice with functions to override default command behavior. +// Functions can be added from the `init()` function in manually curated files in this directory. +var cmdOverrides []func(*cobra.Command) + +func New() *cobra.Command { + cmd := &cobra.Command{ + Use: "lakehouse-monitors", + Short: `A monitor computes and monitors data or model quality metrics for a table over time.`, + Long: `A monitor computes and monitors data or model quality metrics for a table over + time. It generates metrics tables and a dashboard that you can use to monitor + table health and set alerts. + + Most write operations require the user to be the owner of the table (or its + parent schema or parent catalog). Viewing the dashboard, computed metrics, or + monitor configuration only requires the user to have **SELECT** privileges on + the table (along with **USE_SCHEMA** and **USE_CATALOG**).`, + GroupID: "catalog", + Annotations: map[string]string{ + "package": "catalog", + }, + } + + // Apply optional overrides to this command. + for _, fn := range cmdOverrides { + fn(cmd) + } + + return cmd +} + +// start create command + +// Slice with functions to override default command behavior. +// Functions can be added from the `init()` function in manually curated files in this directory. +var createOverrides []func( + *cobra.Command, + *catalog.CreateMonitor, +) + +func newCreate() *cobra.Command { + cmd := &cobra.Command{} + + var createReq catalog.CreateMonitor + var createJson flags.JsonFlag + + // TODO: short flags + cmd.Flags().Var(&createJson, "json", `either inline JSON string or @path/to/file.json with request body`) + + cmd.Flags().StringVar(&createReq.BaselineTableName, "baseline-table-name", createReq.BaselineTableName, `Name of the baseline table from which drift metrics are computed from.`) + // TODO: array: custom_metrics + // TODO: complex arg: data_classification_config + // TODO: complex arg: inference_log + // TODO: array: notifications + // TODO: complex arg: schedule + cmd.Flags().BoolVar(&createReq.SkipBuiltinDashboard, "skip-builtin-dashboard", createReq.SkipBuiltinDashboard, `Whether to skip creating a default dashboard summarizing data quality metrics.`) + // TODO: array: slicing_exprs + // TODO: output-only field + // TODO: complex arg: time_series + cmd.Flags().StringVar(&createReq.WarehouseId, "warehouse-id", createReq.WarehouseId, `Optional argument to specify the warehouse for dashboard creation.`) + + cmd.Use = "create FULL_NAME ASSETS_DIR OUTPUT_SCHEMA_NAME" + cmd.Short = `Create a table monitor.` + cmd.Long = `Create a table monitor. + + Creates a new monitor for the specified table. + + The caller must either: 1. be an owner of the table's parent catalog, have + **USE_SCHEMA** on the table's parent schema, and have **SELECT** access on the + table 2. have **USE_CATALOG** on the table's parent catalog, be an owner of + the table's parent schema, and have **SELECT** access on the table. 3. have + the following permissions: - **USE_CATALOG** on the table's parent catalog - + **USE_SCHEMA** on the table's parent schema - be an owner of the table. + + Workspace assets, such as the dashboard, will be created in the workspace + where this call was made. + + Arguments: + FULL_NAME: Full name of the table. + ASSETS_DIR: The directory to store monitoring assets (e.g. dashboard, metric tables). + OUTPUT_SCHEMA_NAME: Schema where output metric tables are created.` + + cmd.Annotations = make(map[string]string) + + cmd.Args = func(cmd *cobra.Command, args []string) error { + if cmd.Flags().Changed("json") { + err := cobra.ExactArgs(1)(cmd, args) + if err != nil { + return fmt.Errorf("when --json flag is specified, provide only FULL_NAME as positional arguments. Provide 'assets_dir', 'output_schema_name' in your JSON input") + } + return nil + } + check := cobra.ExactArgs(3) + return check(cmd, args) + } + + cmd.PreRunE = root.MustWorkspaceClient + cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { + ctx := cmd.Context() + w := root.WorkspaceClient(ctx) + + if cmd.Flags().Changed("json") { + err = createJson.Unmarshal(&createReq) + if err != nil { + return err + } + } + createReq.FullName = args[0] + if !cmd.Flags().Changed("json") { + createReq.AssetsDir = args[1] + } + if !cmd.Flags().Changed("json") { + createReq.OutputSchemaName = args[2] + } + + response, err := w.LakehouseMonitors.Create(ctx, createReq) + if err != nil { + return err + } + return cmdio.Render(ctx, response) + } + + // Disable completions since they are not applicable. + // Can be overridden by manual implementation in `override.go`. + cmd.ValidArgsFunction = cobra.NoFileCompletions + + // Apply optional overrides to this command. + for _, fn := range createOverrides { + fn(cmd, &createReq) + } + + return cmd +} + +func init() { + cmdOverrides = append(cmdOverrides, func(cmd *cobra.Command) { + cmd.AddCommand(newCreate()) + }) +} + +// start delete command + +// Slice with functions to override default command behavior. +// Functions can be added from the `init()` function in manually curated files in this directory. +var deleteOverrides []func( + *cobra.Command, + *catalog.DeleteLakehouseMonitorRequest, +) + +func newDelete() *cobra.Command { + cmd := &cobra.Command{} + + var deleteReq catalog.DeleteLakehouseMonitorRequest + + // TODO: short flags + + cmd.Use = "delete FULL_NAME" + cmd.Short = `Delete a table monitor.` + cmd.Long = `Delete a table monitor. + + Deletes a monitor for the specified table. + + The caller must either: 1. be an owner of the table's parent catalog 2. have + **USE_CATALOG** on the table's parent catalog and be an owner of the table's + parent schema 3. have the following permissions: - **USE_CATALOG** on the + table's parent catalog - **USE_SCHEMA** on the table's parent schema - be an + owner of the table. + + Additionally, the call must be made from the workspace where the monitor was + created. + + Note that the metric tables and dashboard will not be deleted as part of this + call; those assets must be manually cleaned up (if desired). + + Arguments: + FULL_NAME: Full name of the table.` + + cmd.Annotations = make(map[string]string) + + cmd.Args = func(cmd *cobra.Command, args []string) error { + check := cobra.ExactArgs(1) + return check(cmd, args) + } + + cmd.PreRunE = root.MustWorkspaceClient + cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { + ctx := cmd.Context() + w := root.WorkspaceClient(ctx) + + deleteReq.FullName = args[0] + + err = w.LakehouseMonitors.Delete(ctx, deleteReq) + if err != nil { + return err + } + return nil + } + + // Disable completions since they are not applicable. + // Can be overridden by manual implementation in `override.go`. + cmd.ValidArgsFunction = cobra.NoFileCompletions + + // Apply optional overrides to this command. + for _, fn := range deleteOverrides { + fn(cmd, &deleteReq) + } + + return cmd +} + +func init() { + cmdOverrides = append(cmdOverrides, func(cmd *cobra.Command) { + cmd.AddCommand(newDelete()) + }) +} + +// start get command + +// Slice with functions to override default command behavior. +// Functions can be added from the `init()` function in manually curated files in this directory. +var getOverrides []func( + *cobra.Command, + *catalog.GetLakehouseMonitorRequest, +) + +func newGet() *cobra.Command { + cmd := &cobra.Command{} + + var getReq catalog.GetLakehouseMonitorRequest + + // TODO: short flags + + cmd.Use = "get FULL_NAME" + cmd.Short = `Get a table monitor.` + cmd.Long = `Get a table monitor. + + Gets a monitor for the specified table. + + The caller must either: 1. be an owner of the table's parent catalog 2. have + **USE_CATALOG** on the table's parent catalog and be an owner of the table's + parent schema. 3. have the following permissions: - **USE_CATALOG** on the + table's parent catalog - **USE_SCHEMA** on the table's parent schema - + **SELECT** privilege on the table. + + The returned information includes configuration values, as well as information + on assets created by the monitor. Some information (e.g., dashboard) may be + filtered out if the caller is in a different workspace than where the monitor + was created. + + Arguments: + FULL_NAME: Full name of the table.` + + cmd.Annotations = make(map[string]string) + + cmd.Args = func(cmd *cobra.Command, args []string) error { + check := cobra.ExactArgs(1) + return check(cmd, args) + } + + cmd.PreRunE = root.MustWorkspaceClient + cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { + ctx := cmd.Context() + w := root.WorkspaceClient(ctx) + + getReq.FullName = args[0] + + response, err := w.LakehouseMonitors.Get(ctx, getReq) + if err != nil { + return err + } + return cmdio.Render(ctx, response) + } + + // Disable completions since they are not applicable. + // Can be overridden by manual implementation in `override.go`. + cmd.ValidArgsFunction = cobra.NoFileCompletions + + // Apply optional overrides to this command. + for _, fn := range getOverrides { + fn(cmd, &getReq) + } + + return cmd +} + +func init() { + cmdOverrides = append(cmdOverrides, func(cmd *cobra.Command) { + cmd.AddCommand(newGet()) + }) +} + +// start update command + +// Slice with functions to override default command behavior. +// Functions can be added from the `init()` function in manually curated files in this directory. +var updateOverrides []func( + *cobra.Command, + *catalog.UpdateMonitor, +) + +func newUpdate() *cobra.Command { + cmd := &cobra.Command{} + + var updateReq catalog.UpdateMonitor + var updateJson flags.JsonFlag + + // TODO: short flags + cmd.Flags().Var(&updateJson, "json", `either inline JSON string or @path/to/file.json with request body`) + + cmd.Flags().StringVar(&updateReq.BaselineTableName, "baseline-table-name", updateReq.BaselineTableName, `Name of the baseline table from which drift metrics are computed from.`) + // TODO: array: custom_metrics + // TODO: complex arg: data_classification_config + // TODO: complex arg: inference_log + // TODO: array: notifications + // TODO: complex arg: schedule + // TODO: array: slicing_exprs + // TODO: output-only field + // TODO: complex arg: time_series + + cmd.Use = "update FULL_NAME ASSETS_DIR OUTPUT_SCHEMA_NAME" + cmd.Short = `Update a table monitor.` + cmd.Long = `Update a table monitor. + + Updates a monitor for the specified table. + + The caller must either: 1. be an owner of the table's parent catalog 2. have + **USE_CATALOG** on the table's parent catalog and be an owner of the table's + parent schema 3. have the following permissions: - **USE_CATALOG** on the + table's parent catalog - **USE_SCHEMA** on the table's parent schema - be an + owner of the table. + + Additionally, the call must be made from the workspace where the monitor was + created, and the caller must be the original creator of the monitor. + + Certain configuration fields, such as output asset identifiers, cannot be + updated. + + Arguments: + FULL_NAME: Full name of the table. + ASSETS_DIR: The directory to store monitoring assets (e.g. dashboard, metric tables). + OUTPUT_SCHEMA_NAME: Schema where output metric tables are created.` + + cmd.Annotations = make(map[string]string) + + cmd.Args = func(cmd *cobra.Command, args []string) error { + if cmd.Flags().Changed("json") { + err := cobra.ExactArgs(1)(cmd, args) + if err != nil { + return fmt.Errorf("when --json flag is specified, provide only FULL_NAME as positional arguments. Provide 'assets_dir', 'output_schema_name' in your JSON input") + } + return nil + } + check := cobra.ExactArgs(3) + return check(cmd, args) + } + + cmd.PreRunE = root.MustWorkspaceClient + cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { + ctx := cmd.Context() + w := root.WorkspaceClient(ctx) + + if cmd.Flags().Changed("json") { + err = updateJson.Unmarshal(&updateReq) + if err != nil { + return err + } + } + updateReq.FullName = args[0] + if !cmd.Flags().Changed("json") { + updateReq.AssetsDir = args[1] + } + if !cmd.Flags().Changed("json") { + updateReq.OutputSchemaName = args[2] + } + + response, err := w.LakehouseMonitors.Update(ctx, updateReq) + if err != nil { + return err + } + return cmdio.Render(ctx, response) + } + + // Disable completions since they are not applicable. + // Can be overridden by manual implementation in `override.go`. + cmd.ValidArgsFunction = cobra.NoFileCompletions + + // Apply optional overrides to this command. + for _, fn := range updateOverrides { + fn(cmd, &updateReq) + } + + return cmd +} + +func init() { + cmdOverrides = append(cmdOverrides, func(cmd *cobra.Command) { + cmd.AddCommand(newUpdate()) + }) +} + +// end service LakehouseMonitors diff --git a/cmd/workspace/libraries/libraries.go b/cmd/workspace/libraries/libraries.go index 82806421e51..e7174cadaba 100755 --- a/cmd/workspace/libraries/libraries.go +++ b/cmd/workspace/libraries/libraries.go @@ -158,7 +158,6 @@ func newClusterStatus() *cobra.Command { clusterStatusReq.ClusterId = args[0] response := w.Libraries.ClusterStatus(ctx, clusterStatusReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/metastores/metastores.go b/cmd/workspace/metastores/metastores.go index c42f1b0ae09..081eb735084 100755 --- a/cmd/workspace/metastores/metastores.go +++ b/cmd/workspace/metastores/metastores.go @@ -456,7 +456,6 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.Metastores.List(ctx) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/model-registry/model-registry.go b/cmd/workspace/model-registry/model-registry.go index fc303b66bfa..2c21ca14852 100755 --- a/cmd/workspace/model-registry/model-registry.go +++ b/cmd/workspace/model-registry/model-registry.go @@ -1129,7 +1129,6 @@ func newGetLatestVersions() *cobra.Command { } response := w.ModelRegistry.GetLatestVersions(ctx, getLatestVersionsReq) - return cmdio.Render(ctx, response) } @@ -1519,7 +1518,6 @@ func newListModels() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.ModelRegistry.ListModels(ctx, listModelsReq) - return cmdio.Render(ctx, response) } @@ -1583,7 +1581,6 @@ func newListTransitionRequests() *cobra.Command { listTransitionRequestsReq.Version = args[1] response := w.ModelRegistry.ListTransitionRequests(ctx, listTransitionRequestsReq) - return cmdio.Render(ctx, response) } @@ -1646,7 +1643,6 @@ func newListWebhooks() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.ModelRegistry.ListWebhooks(ctx, listWebhooksReq) - return cmdio.Render(ctx, response) } @@ -1893,7 +1889,6 @@ func newSearchModelVersions() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.ModelRegistry.SearchModelVersions(ctx, searchModelVersionsReq) - return cmdio.Render(ctx, response) } @@ -1955,7 +1950,6 @@ func newSearchModels() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.ModelRegistry.SearchModels(ctx, searchModelsReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/model-versions/model-versions.go b/cmd/workspace/model-versions/model-versions.go index 743bd836242..8b1787c683b 100755 --- a/cmd/workspace/model-versions/model-versions.go +++ b/cmd/workspace/model-versions/model-versions.go @@ -316,7 +316,6 @@ func newList() *cobra.Command { listReq.FullName = args[0] response := w.ModelVersions.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/pipelines/pipelines.go b/cmd/workspace/pipelines/pipelines.go index 501ccbc2a06..8cff417e96a 100755 --- a/cmd/workspace/pipelines/pipelines.go +++ b/cmd/workspace/pipelines/pipelines.go @@ -537,7 +537,6 @@ func newListPipelineEvents() *cobra.Command { listPipelineEventsReq.PipelineId = args[0] response := w.Pipelines.ListPipelineEvents(ctx, listPipelineEventsReq) - return cmdio.Render(ctx, response) } @@ -599,7 +598,6 @@ func newListPipelines() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Pipelines.ListPipelines(ctx, listPipelinesReq) - return cmdio.Render(ctx, response) } @@ -907,6 +905,7 @@ func newStartUpdate() *cobra.Command { cmd.Flags().BoolVar(&startUpdateReq.FullRefresh, "full-refresh", startUpdateReq.FullRefresh, `If true, this update will reset all tables before running.`) // TODO: array: full_refresh_selection // TODO: array: refresh_selection + cmd.Flags().BoolVar(&startUpdateReq.ValidateOnly, "validate-only", startUpdateReq.ValidateOnly, `If true, this update only validates the correctness of pipeline source code but does not materialize or publish any datasets.`) cmd.Use = "start-update PIPELINE_ID" cmd.Short = `Start a pipeline.` diff --git a/cmd/workspace/policy-families/policy-families.go b/cmd/workspace/policy-families/policy-families.go index d76e0a15353..d6dbc45a3e8 100755 --- a/cmd/workspace/policy-families/policy-families.go +++ b/cmd/workspace/policy-families/policy-families.go @@ -139,7 +139,6 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.PolicyFamilies.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/providers/providers.go b/cmd/workspace/providers/providers.go index b127a26ef0f..9a5bf86a230 100755 --- a/cmd/workspace/providers/providers.go +++ b/cmd/workspace/providers/providers.go @@ -324,7 +324,6 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Providers.List(ctx, listReq) - return cmdio.Render(ctx, response) } @@ -400,7 +399,6 @@ func newListShares() *cobra.Command { listSharesReq.Name = args[0] response := w.Providers.ListShares(ctx, listSharesReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/queries/queries.go b/cmd/workspace/queries/queries.go index 53634efeb22..ccb532ec756 100755 --- a/cmd/workspace/queries/queries.go +++ b/cmd/workspace/queries/queries.go @@ -286,7 +286,10 @@ func newList() *cobra.Command { cmd.Long = `Get a list of queries. Gets a list of queries. Optionally, this list can be filtered by a search - term.` + term. + + ### **Warning: Calling this API concurrently 10 or more times could result in + throttling, service degradation, or a temporary ban.**` cmd.Annotations = make(map[string]string) @@ -301,7 +304,6 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Queries.List(ctx, listReq) - return cmdio.Render(ctx, response) } @@ -420,6 +422,7 @@ func newUpdate() *cobra.Command { cmd.Flags().StringVar(&updateReq.Name, "name", updateReq.Name, `The title of this query that appears in list views, widget headings, and on the query page.`) // TODO: any: options cmd.Flags().StringVar(&updateReq.Query, "query", updateReq.Query, `The text of the query to be run.`) + cmd.Flags().Var(&updateReq.RunAsRole, "run-as-role", `Sets the **Run as** role for the object. Supported values: [owner, viewer]`) cmd.Use = "update QUERY_ID" cmd.Short = `Change a query definition.` diff --git a/cmd/workspace/query-history/query-history.go b/cmd/workspace/query-history/query-history.go index 39305ae1477..88d4a99cfd4 100755 --- a/cmd/workspace/query-history/query-history.go +++ b/cmd/workspace/query-history/query-history.go @@ -74,7 +74,6 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.QueryHistory.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/recipients/recipients.go b/cmd/workspace/recipients/recipients.go index d701a748fbf..112d1675091 100755 --- a/cmd/workspace/recipients/recipients.go +++ b/cmd/workspace/recipients/recipients.go @@ -343,7 +343,6 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Recipients.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/registered-models/registered-models.go b/cmd/workspace/registered-models/registered-models.go index 679aac06cd0..7663b6e5e27 100755 --- a/cmd/workspace/registered-models/registered-models.go +++ b/cmd/workspace/registered-models/registered-models.go @@ -451,7 +451,6 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.RegisteredModels.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/repos/repos.go b/cmd/workspace/repos/repos.go index 9f4aadd2707..022e51a0c91 100755 --- a/cmd/workspace/repos/repos.go +++ b/cmd/workspace/repos/repos.go @@ -486,7 +486,6 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Repos.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/schemas/schemas.go b/cmd/workspace/schemas/schemas.go index 046b366bae4..33df65b4611 100755 --- a/cmd/workspace/schemas/schemas.go +++ b/cmd/workspace/schemas/schemas.go @@ -334,7 +334,6 @@ func newList() *cobra.Command { listReq.CatalogName = args[0] response := w.Schemas.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/secrets/secrets.go b/cmd/workspace/secrets/secrets.go index 958b704dc77..c3ed525762e 100755 --- a/cmd/workspace/secrets/secrets.go +++ b/cmd/workspace/secrets/secrets.go @@ -591,7 +591,6 @@ func newListAcls() *cobra.Command { listAclsReq.Scope = args[0] response := w.Secrets.ListAcls(ctx, listAclsReq) - return cmdio.Render(ctx, response) } @@ -640,7 +639,6 @@ func newListScopes() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.Secrets.ListScopes(ctx) - return cmdio.Render(ctx, response) } @@ -709,7 +707,6 @@ func newListSecrets() *cobra.Command { listSecretsReq.Scope = args[0] response := w.Secrets.ListSecrets(ctx, listSecretsReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/service-principals/service-principals.go b/cmd/workspace/service-principals/service-principals.go index 1e141653be5..832c81d9ce2 100755 --- a/cmd/workspace/service-principals/service-principals.go +++ b/cmd/workspace/service-principals/service-principals.go @@ -314,7 +314,6 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.ServicePrincipals.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/serving-endpoints/serving-endpoints.go b/cmd/workspace/serving-endpoints/serving-endpoints.go index 2fc8006a9db..b29bb9519a8 100755 --- a/cmd/workspace/serving-endpoints/serving-endpoints.go +++ b/cmd/workspace/serving-endpoints/serving-endpoints.go @@ -544,7 +544,6 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.ServingEndpoints.List(ctx) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/shares/shares.go b/cmd/workspace/shares/shares.go index e014d89d05e..9ab37d42e9a 100755 --- a/cmd/workspace/shares/shares.go +++ b/cmd/workspace/shares/shares.go @@ -282,7 +282,6 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.Shares.List(ctx) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/storage-credentials/storage-credentials.go b/cmd/workspace/storage-credentials/storage-credentials.go index dcb0eb7c0b7..c86fb62529b 100755 --- a/cmd/workspace/storage-credentials/storage-credentials.go +++ b/cmd/workspace/storage-credentials/storage-credentials.go @@ -337,7 +337,6 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.StorageCredentials.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/system-schemas/system-schemas.go b/cmd/workspace/system-schemas/system-schemas.go index d46b25104f6..1d10f335b06 100755 --- a/cmd/workspace/system-schemas/system-schemas.go +++ b/cmd/workspace/system-schemas/system-schemas.go @@ -217,7 +217,6 @@ func newList() *cobra.Command { listReq.MetastoreId = args[0] response := w.SystemSchemas.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/tables/tables.go b/cmd/workspace/tables/tables.go index f1d08960686..a840fd23363 100755 --- a/cmd/workspace/tables/tables.go +++ b/cmd/workspace/tables/tables.go @@ -123,6 +123,89 @@ func init() { }) } +// start exists command + +// Slice with functions to override default command behavior. +// Functions can be added from the `init()` function in manually curated files in this directory. +var existsOverrides []func( + *cobra.Command, + *catalog.ExistsRequest, +) + +func newExists() *cobra.Command { + cmd := &cobra.Command{} + + var existsReq catalog.ExistsRequest + + // TODO: short flags + + cmd.Use = "exists FULL_NAME" + cmd.Short = `Get boolean reflecting if table exists.` + cmd.Long = `Get boolean reflecting if table exists. + + Gets if a table exists in the metastore for a specific catalog and schema. The + caller must satisfy one of the following requirements: * Be a metastore admin + * Be the owner of the parent catalog * Be the owner of the parent schema and + have the USE_CATALOG privilege on the parent catalog * Have the + **USE_CATALOG** privilege on the parent catalog and the **USE_SCHEMA** + privilege on the parent schema, and either be the table owner or have the + SELECT privilege on the table. * Have BROWSE privilege on the parent catalog * + Have BROWSE privilege on the parent schema. + + Arguments: + FULL_NAME: Full name of the table.` + + cmd.Annotations = make(map[string]string) + + cmd.PreRunE = root.MustWorkspaceClient + cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { + ctx := cmd.Context() + w := root.WorkspaceClient(ctx) + + if len(args) == 0 { + promptSpinner := cmdio.Spinner(ctx) + promptSpinner <- "No FULL_NAME argument specified. Loading names for Tables drop-down." + names, err := w.Tables.TableInfoNameToTableIdMap(ctx, catalog.ListTablesRequest{}) + close(promptSpinner) + if err != nil { + return fmt.Errorf("failed to load names for Tables drop-down. Please manually specify required arguments. Original error: %w", err) + } + id, err := cmdio.Select(ctx, names, "Full name of the table") + if err != nil { + return err + } + args = append(args, id) + } + if len(args) != 1 { + return fmt.Errorf("expected to have full name of the table") + } + existsReq.FullName = args[0] + + response, err := w.Tables.Exists(ctx, existsReq) + if err != nil { + return err + } + return cmdio.Render(ctx, response) + } + + // Disable completions since they are not applicable. + // Can be overridden by manual implementation in `override.go`. + cmd.ValidArgsFunction = cobra.NoFileCompletions + + // Apply optional overrides to this command. + for _, fn := range existsOverrides { + fn(cmd, &existsReq) + } + + return cmd +} + +func init() { + cmdOverrides = append(cmdOverrides, func(cmd *cobra.Command) { + cmd.AddCommand(newExists()) + }) +} + // start get command // Slice with functions to override default command behavior. @@ -146,10 +229,12 @@ func newGet() *cobra.Command { cmd.Long = `Get a table. Gets a table from the metastore for a specific catalog and schema. The caller - must be a metastore admin, be the owner of the table and have the - **USE_CATALOG** privilege on the parent catalog and the **USE_SCHEMA** - privilege on the parent schema, or be the owner of the table and have the - **SELECT** privilege on it as well. + must satisfy one of the following requirements: * Be a metastore admin * Be + the owner of the parent catalog * Be the owner of the parent schema and have + the USE_CATALOG privilege on the parent catalog * Have the **USE_CATALOG** + privilege on the parent catalog and the **USE_SCHEMA** privilege on the parent + schema, and either be the table owner or have the SELECT privilege on the + table. Arguments: FULL_NAME: Full name of the table.` @@ -258,7 +343,6 @@ func newList() *cobra.Command { listReq.SchemaName = args[1] response := w.Tables.List(ctx, listReq) - return cmdio.Render(ctx, response) } @@ -347,7 +431,6 @@ func newListSummaries() *cobra.Command { listSummariesReq.CatalogName = args[0] response := w.Tables.ListSummaries(ctx, listSummariesReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/token-management/token-management.go b/cmd/workspace/token-management/token-management.go index d9e6251178b..c013181fe64 100755 --- a/cmd/workspace/token-management/token-management.go +++ b/cmd/workspace/token-management/token-management.go @@ -56,16 +56,16 @@ func newCreateOboToken() *cobra.Command { cmd.Flags().Var(&createOboTokenJson, "json", `either inline JSON string or @path/to/file.json with request body`) cmd.Flags().StringVar(&createOboTokenReq.Comment, "comment", createOboTokenReq.Comment, `Comment that describes the purpose of the token.`) + cmd.Flags().Int64Var(&createOboTokenReq.LifetimeSeconds, "lifetime-seconds", createOboTokenReq.LifetimeSeconds, `The number of seconds before the token expires.`) - cmd.Use = "create-obo-token APPLICATION_ID LIFETIME_SECONDS" + cmd.Use = "create-obo-token APPLICATION_ID" cmd.Short = `Create on-behalf token.` cmd.Long = `Create on-behalf token. - + Creates a token on behalf of a service principal. Arguments: - APPLICATION_ID: Application ID of the service principal. - LIFETIME_SECONDS: The number of seconds before the token expires.` + APPLICATION_ID: Application ID of the service principal.` cmd.Annotations = make(map[string]string) @@ -73,12 +73,11 @@ func newCreateOboToken() *cobra.Command { if cmd.Flags().Changed("json") { err := cobra.ExactArgs(0)(cmd, args) if err != nil { - return fmt.Errorf("when --json flag is specified, no positional arguments are required. Provide 'application_id', 'lifetime_seconds' in your JSON input") + return fmt.Errorf("when --json flag is specified, no positional arguments are required. Provide 'application_id' in your JSON input") } return nil } - check := cobra.ExactArgs(2) - return check(cmd, args) + return nil } cmd.PreRunE = root.MustWorkspaceClient @@ -91,15 +90,25 @@ func newCreateOboToken() *cobra.Command { if err != nil { return err } - } - if !cmd.Flags().Changed("json") { - createOboTokenReq.ApplicationId = args[0] - } - if !cmd.Flags().Changed("json") { - _, err = fmt.Sscan(args[1], &createOboTokenReq.LifetimeSeconds) - if err != nil { - return fmt.Errorf("invalid LIFETIME_SECONDS: %s", args[1]) + } else { + if len(args) == 0 { + promptSpinner := cmdio.Spinner(ctx) + promptSpinner <- "No APPLICATION_ID argument specified. Loading names for Token Management drop-down." + names, err := w.TokenManagement.TokenInfoCommentToTokenIdMap(ctx, settings.ListTokenManagementRequest{}) + close(promptSpinner) + if err != nil { + return fmt.Errorf("failed to load names for Token Management drop-down. Please manually specify required arguments. Original error: %w", err) + } + id, err := cmdio.Select(ctx, names, "Application ID of the service principal") + if err != nil { + return err + } + args = append(args, id) } + if len(args) != 1 { + return fmt.Errorf("expected to have application id of the service principal") + } + createOboTokenReq.ApplicationId = args[0] } response, err := w.TokenManagement.CreateOboToken(ctx, createOboTokenReq) @@ -146,7 +155,7 @@ func newDelete() *cobra.Command { cmd.Use = "delete TOKEN_ID" cmd.Short = `Delete a token.` cmd.Long = `Delete a token. - + Deletes a token, specified by its ID. Arguments: @@ -222,7 +231,7 @@ func newGet() *cobra.Command { cmd.Use = "get TOKEN_ID" cmd.Short = `Get token info.` cmd.Long = `Get token info. - + Gets information about a token, specified by its ID. Arguments: @@ -293,7 +302,7 @@ func newGetPermissionLevels() *cobra.Command { cmd.Use = "get-permission-levels" cmd.Short = `Get token permission levels.` cmd.Long = `Get token permission levels. - + Gets the permission levels that a user can have on an object.` cmd.Annotations = make(map[string]string) @@ -341,7 +350,7 @@ func newGetPermissions() *cobra.Command { cmd.Use = "get-permissions" cmd.Short = `Get token permissions.` cmd.Long = `Get token permissions. - + Gets the permissions of all tokens. Tokens can inherit permissions from their root object.` @@ -398,7 +407,7 @@ func newList() *cobra.Command { cmd.Use = "list" cmd.Short = `List all tokens.` cmd.Long = `List all tokens. - + Lists all tokens associated with the specified workspace or user.` cmd.Annotations = make(map[string]string) @@ -414,7 +423,6 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.TokenManagement.List(ctx, listReq) - return cmdio.Render(ctx, response) } @@ -459,7 +467,7 @@ func newSetPermissions() *cobra.Command { cmd.Use = "set-permissions" cmd.Short = `Set token permissions.` cmd.Long = `Set token permissions. - + Sets permissions on all tokens. Tokens can inherit permissions from their root object.` @@ -530,7 +538,7 @@ func newUpdatePermissions() *cobra.Command { cmd.Use = "update-permissions" cmd.Short = `Update token permissions.` cmd.Long = `Update token permissions. - + Updates the permissions on all tokens. Tokens can inherit permissions from their root object.` diff --git a/cmd/workspace/tokens/tokens.go b/cmd/workspace/tokens/tokens.go index 357f51f1ca5..cb2a92981e9 100755 --- a/cmd/workspace/tokens/tokens.go +++ b/cmd/workspace/tokens/tokens.go @@ -233,7 +233,6 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.Tokens.List(ctx) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/users/users.go b/cmd/workspace/users/users.go index 7d831b701ae..b1e54fb1186 100755 --- a/cmd/workspace/users/users.go +++ b/cmd/workspace/users/users.go @@ -427,7 +427,6 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Users.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/vector-search-endpoints/vector-search-endpoints.go b/cmd/workspace/vector-search-endpoints/vector-search-endpoints.go index e79101a6614..2d6324340c5 100755 --- a/cmd/workspace/vector-search-endpoints/vector-search-endpoints.go +++ b/cmd/workspace/vector-search-endpoints/vector-search-endpoints.go @@ -309,7 +309,6 @@ func newListEndpoints() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.VectorSearchEndpoints.ListEndpoints(ctx, listEndpointsReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/vector-search-indexes/vector-search-indexes.go b/cmd/workspace/vector-search-indexes/vector-search-indexes.go index bd890d5b1c5..dfe08a8c331 100755 --- a/cmd/workspace/vector-search-indexes/vector-search-indexes.go +++ b/cmd/workspace/vector-search-indexes/vector-search-indexes.go @@ -390,7 +390,6 @@ func newListIndexes() *cobra.Command { listIndexesReq.EndpointName = args[0] response := w.VectorSearchIndexes.ListIndexes(ctx, listIndexesReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/volumes/volumes.go b/cmd/workspace/volumes/volumes.go index 0b6ed98cddc..098911745c4 100755 --- a/cmd/workspace/volumes/volumes.go +++ b/cmd/workspace/volumes/volumes.go @@ -293,7 +293,6 @@ func newList() *cobra.Command { listReq.SchemaName = args[1] response := w.Volumes.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/warehouses/warehouses.go b/cmd/workspace/warehouses/warehouses.go index 2d39e979bbe..ecf5d634925 100755 --- a/cmd/workspace/warehouses/warehouses.go +++ b/cmd/workspace/warehouses/warehouses.go @@ -662,7 +662,6 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Warehouses.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/cmd/workspace/workspace/workspace.go b/cmd/workspace/workspace/workspace.go index e863a3d5782..6168fb35c0d 100755 --- a/cmd/workspace/workspace/workspace.go +++ b/cmd/workspace/workspace/workspace.go @@ -578,7 +578,6 @@ func newList() *cobra.Command { listReq.Path = args[0] response := w.Workspace.List(ctx, listReq) - return cmdio.Render(ctx, response) } diff --git a/libs/cmdio/reflect_iterator.go b/libs/cmdio/reflect_iterator.go index af47dc7ee50..56d84a575dd 100644 --- a/libs/cmdio/reflect_iterator.go +++ b/libs/cmdio/reflect_iterator.go @@ -3,8 +3,6 @@ package cmdio import ( "context" "reflect" - - "github.com/databricks/databricks-sdk-go/listing" ) // Reflectively call Next and HasNext on listing.Iterator[*] values. @@ -45,5 +43,3 @@ func (r reflectIterator) Next(ctx context.Context) (any, error) { } return item, res[1].Interface().(error) } - -var _ listing.Iterator[any] = reflectIterator{} diff --git a/libs/cmdio/render.go b/libs/cmdio/render.go index 5afc8cbee4a..42c7317e4df 100644 --- a/libs/cmdio/render.go +++ b/libs/cmdio/render.go @@ -193,7 +193,7 @@ func (d defaultRenderer) renderTemplate(_ context.Context, t *template.Template, // - textRenderer // - templateRenderer func newRenderer(it any) any { - if r, ok := any(it).(io.Reader); ok { + if r, ok := it.(io.Reader); ok { return readerRenderer{reader: r} } if iterator, ok := newReflectIterator(it); ok { @@ -208,7 +208,7 @@ type bufferedFlusher struct { } func (b bufferedFlusher) Write(bs []byte) (int, error) { - return b.w.Write(bs) + return b.b.Write(bs) } func (b bufferedFlusher) Flush() error { @@ -261,7 +261,7 @@ func RenderWithTemplate(ctx context.Context, v any, headerTemplate, template str func RenderJson(ctx context.Context, v any) error { c := fromContext(ctx) if jr, ok := newRenderer(v).(jsonRenderer); ok { - jr.renderJson(ctx, newBufferedFlusher(c.out)) + return jr.renderJson(ctx, newBufferedFlusher(c.out)) } return errors.New("json output not supported") } @@ -269,7 +269,7 @@ func RenderJson(ctx context.Context, v any) error { func RenderReader(ctx context.Context, r io.Reader) error { c := fromContext(ctx) if jr, ok := newRenderer(r).(textRenderer); ok { - jr.renderText(ctx, newBufferedFlusher(c.out)) + return jr.renderText(ctx, newBufferedFlusher(c.out)) } return errors.New("rendering io.Reader not supported") } From 44d764bd897fd942e11d7e03981820418762317f Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Tue, 13 Feb 2024 06:05:03 -0800 Subject: [PATCH 07/13] manual fixes... --- .codegen/_openapi_sha | 2 +- .gitattributes | 1 - bundle/schema/docs/bundle_descriptions.json | 92 +++++++++--------- .../cluster-policies/cluster-policies.go | 9 +- cmd/workspace/clusters/clusters.go | 5 +- cmd/workspace/cmd.go | 2 - cmd/workspace/dashboards/dashboards.go | 95 +------------------ cmd/workspace/experiments/experiments.go | 8 +- .../global-init-scripts.go | 2 +- cmd/workspace/pipelines/pipelines.go | 1 - cmd/workspace/queries/queries.go | 6 +- cmd/workspace/tables/tables.go | 93 +----------------- .../token-management/token-management.go | 53 +++++------ 13 files changed, 87 insertions(+), 282 deletions(-) diff --git a/.codegen/_openapi_sha b/.codegen/_openapi_sha index f705ffea629..56c8253ff88 100644 --- a/.codegen/_openapi_sha +++ b/.codegen/_openapi_sha @@ -1 +1 @@ -e05401ed5dd4974c5333d737ec308a7d451f749f \ No newline at end of file +a7a9dc025bb80303e676bf3708942c6aa06689f1 \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index fe33227a7e7..7a1750caa83 100755 --- a/.gitattributes +++ b/.gitattributes @@ -50,7 +50,6 @@ cmd/workspace/instance-pools/instance-pools.go linguist-generated=true cmd/workspace/instance-profiles/instance-profiles.go linguist-generated=true cmd/workspace/ip-access-lists/ip-access-lists.go linguist-generated=true cmd/workspace/jobs/jobs.go linguist-generated=true -cmd/workspace/lakehouse-monitors/lakehouse-monitors.go linguist-generated=true cmd/workspace/lakeview/lakeview.go linguist-generated=true cmd/workspace/libraries/libraries.go linguist-generated=true cmd/workspace/metastores/metastores.go linguist-generated=true diff --git a/bundle/schema/docs/bundle_descriptions.json b/bundle/schema/docs/bundle_descriptions.json index 2308846401b..4694f8ed290 100644 --- a/bundle/schema/docs/bundle_descriptions.json +++ b/bundle/schema/docs/bundle_descriptions.json @@ -193,7 +193,7 @@ "description": "An optional continuous property for this job. The continuous property will ensure that there is always one run executing. Only one of `schedule` and `continuous` can be used.", "properties": { "pause_status": { - "description": "Indicate whether this schedule is paused or not." + "description": "Whether this trigger is paused or not." } } }, @@ -703,7 +703,7 @@ "description": "An optional periodic schedule for this job. The default behavior is that the job only runs when triggered by clicking “Run Now” in the Jobs UI or sending an API request to `runNow`.", "properties": { "pause_status": { - "description": "Indicate whether this schedule is paused or not." + "description": "Whether this trigger is paused or not." }, "quartz_cron_expression": { "description": "A Cron expression using Quartz syntax that describes the schedule for a job.\nSee [Cron Trigger](http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html)\nfor details. This field is required.\"\n" @@ -814,7 +814,7 @@ } }, "existing_cluster_id": { - "description": "If existing_cluster_id, the ID of an existing cluster that is used for all runs of this task. Only all-purpose clusters are supported. When running tasks on an existing cluster, you may need to manually restart the cluster if it stops responding. We suggest running jobs on new clusters for greater reliability." + "description": "If existing_cluster_id, the ID of an existing cluster that is used for all runs of this task. When running tasks on an existing cluster, you may need to manually restart the cluster if it stops responding. We suggest running jobs on new clusters for greater reliability." }, "health": { "description": "", @@ -1428,7 +1428,7 @@ "description": "An optional timeout applied to each run of this job task. A value of `0` means no timeout." }, "webhook_notifications": { - "description": "A collection of system notification IDs to notify when runs of this task begin or complete. The default behavior is to not send any system notifications.", + "description": "A collection of system notification IDs to notify when runs of this job begin or complete.", "properties": { "on_duration_warning_threshold_exceeded": { "description": "An optional list of system notification IDs to call when the duration of a run exceeds the threshold specified for the `RUN_DURATION_SECONDS` metric in the `health` field. A maximum of 3 destinations can be specified for the `on_duration_warning_threshold_exceeded` property.", @@ -1500,12 +1500,12 @@ } }, "pause_status": { - "description": "Indicate whether this schedule is paused or not." + "description": "Whether this trigger is paused or not." } } }, "webhook_notifications": { - "description": "A collection of system notification IDs to notify when runs of this task begin or complete. The default behavior is to not send any system notifications.", + "description": "A collection of system notification IDs to notify when runs of this job begin or complete.", "properties": { "on_duration_warning_threshold_exceeded": { "description": "An optional list of system notification IDs to call when the duration of a run exceeds the threshold specified for the `RUN_DURATION_SECONDS` metric in the `health` field. A maximum of 3 destinations can be specified for the `on_duration_warning_threshold_exceeded` property.", @@ -2105,65 +2105,65 @@ } }, "init_scripts": { - "description": "The configuration for storing init scripts. Any number of destinations can be specified. The scripts are executed sequentially in the order provided. If `cluster_log_conf` is specified, init script logs are sent to `\u003cdestination\u003e/\u003ccluster-ID\u003e/init_scripts`.", + "description": "", "items": { "description": "", "properties": { "dbfs": { - "description": "destination needs to be provided. e.g.\n`{ \"dbfs\" : { \"destination\" : \"dbfs:/home/cluster_log\" } }`", + "description": "", "properties": { "destination": { - "description": "dbfs destination, e.g. `dbfs:/my/path`" + "description": "" } } }, "file": { - "description": "destination needs to be provided. e.g.\n`{ \"file\" : { \"destination\" : \"file:/my/local/file.sh\" } }`", + "description": "", "properties": { "destination": { - "description": "local file destination, e.g. `file:/my/local/file.sh`" + "description": "" } } }, "s3": { - "description": "destination and either the region or endpoint need to be provided. e.g.\n`{ \"s3\": { \"destination\" : \"s3://cluster_log_bucket/prefix\", \"region\" : \"us-west-2\" } }`\nCluster iam role is used to access s3, please make sure the cluster iam role in\n`instance_profile_arn` has permission to write data to the s3 destination.", + "description": "", "properties": { "canned_acl": { - "description": "(Optional) Set canned access control list for the logs, e.g. `bucket-owner-full-control`.\nIf `canned_cal` is set, please make sure the cluster iam role has `s3:PutObjectAcl` permission on\nthe destination bucket and prefix. The full list of possible canned acl can be found at\nhttp://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl.\nPlease also note that by default only the object owner gets full controls. If you are using cross account\nrole for writing data, you may want to set `bucket-owner-full-control` to make bucket owner able to\nread the logs." + "description": "" }, "destination": { - "description": "S3 destination, e.g. `s3://my-bucket/some-prefix` Note that logs will be delivered using\ncluster iam role, please make sure you set cluster iam role and the role has write access to the\ndestination. Please also note that you cannot use AWS keys to deliver logs." + "description": "" }, "enable_encryption": { - "description": "(Optional) Flag to enable server side encryption, `false` by default." + "description": "" }, "encryption_type": { - "description": "(Optional) The encryption type, it could be `sse-s3` or `sse-kms`. It will be used only when\nencryption is enabled and the default type is `sse-s3`." + "description": "" }, "endpoint": { - "description": "S3 endpoint, e.g. `https://s3-us-west-2.amazonaws.com`. Either region or endpoint needs to be set.\nIf both are set, endpoint will be used." + "description": "" }, "kms_key": { - "description": "(Optional) Kms key which will be used if encryption is enabled and encryption type is set to `sse-kms`." + "description": "" }, "region": { - "description": "S3 region, e.g. `us-west-2`. Either region or endpoint needs to be set. If both are set,\nendpoint will be used." + "description": "" } } }, "volumes": { - "description": "destination needs to be provided. e.g.\n`{ \"volumes\" : { \"destination\" : \"/Volumes/my-init.sh\" } }`", + "description": "", "properties": { "destination": { - "description": "Unity Catalog Volumes file destination, e.g. `/Volumes/my-init.sh`" + "description": "" } } }, "workspace": { - "description": "destination needs to be provided. e.g.\n`{ \"workspace\" : { \"destination\" : \"/Users/user1@databricks.com/my-init.sh\" } }`", + "description": "", "properties": { "destination": { - "description": "workspace files destination, e.g. `/Users/user1@databricks.com/my-init.sh`" + "description": "" } } } @@ -2629,7 +2629,7 @@ "description": "An optional continuous property for this job. The continuous property will ensure that there is always one run executing. Only one of `schedule` and `continuous` can be used.", "properties": { "pause_status": { - "description": "Indicate whether this schedule is paused or not." + "description": "Whether this trigger is paused or not." } } }, @@ -3139,7 +3139,7 @@ "description": "An optional periodic schedule for this job. The default behavior is that the job only runs when triggered by clicking “Run Now” in the Jobs UI or sending an API request to `runNow`.", "properties": { "pause_status": { - "description": "Indicate whether this schedule is paused or not." + "description": "Whether this trigger is paused or not." }, "quartz_cron_expression": { "description": "A Cron expression using Quartz syntax that describes the schedule for a job.\nSee [Cron Trigger](http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html)\nfor details. This field is required.\"\n" @@ -3250,7 +3250,7 @@ } }, "existing_cluster_id": { - "description": "If existing_cluster_id, the ID of an existing cluster that is used for all runs of this task. Only all-purpose clusters are supported. When running tasks on an existing cluster, you may need to manually restart the cluster if it stops responding. We suggest running jobs on new clusters for greater reliability." + "description": "If existing_cluster_id, the ID of an existing cluster that is used for all runs of this task. When running tasks on an existing cluster, you may need to manually restart the cluster if it stops responding. We suggest running jobs on new clusters for greater reliability." }, "health": { "description": "", @@ -3864,7 +3864,7 @@ "description": "An optional timeout applied to each run of this job task. A value of `0` means no timeout." }, "webhook_notifications": { - "description": "A collection of system notification IDs to notify when runs of this task begin or complete. The default behavior is to not send any system notifications.", + "description": "A collection of system notification IDs to notify when runs of this job begin or complete.", "properties": { "on_duration_warning_threshold_exceeded": { "description": "An optional list of system notification IDs to call when the duration of a run exceeds the threshold specified for the `RUN_DURATION_SECONDS` metric in the `health` field. A maximum of 3 destinations can be specified for the `on_duration_warning_threshold_exceeded` property.", @@ -3936,12 +3936,12 @@ } }, "pause_status": { - "description": "Indicate whether this schedule is paused or not." + "description": "Whether this trigger is paused or not." } } }, "webhook_notifications": { - "description": "A collection of system notification IDs to notify when runs of this task begin or complete. The default behavior is to not send any system notifications.", + "description": "A collection of system notification IDs to notify when runs of this job begin or complete.", "properties": { "on_duration_warning_threshold_exceeded": { "description": "An optional list of system notification IDs to call when the duration of a run exceeds the threshold specified for the `RUN_DURATION_SECONDS` metric in the `health` field. A maximum of 3 destinations can be specified for the `on_duration_warning_threshold_exceeded` property.", @@ -4541,65 +4541,65 @@ } }, "init_scripts": { - "description": "The configuration for storing init scripts. Any number of destinations can be specified. The scripts are executed sequentially in the order provided. If `cluster_log_conf` is specified, init script logs are sent to `\u003cdestination\u003e/\u003ccluster-ID\u003e/init_scripts`.", + "description": "", "items": { "description": "", "properties": { "dbfs": { - "description": "destination needs to be provided. e.g.\n`{ \"dbfs\" : { \"destination\" : \"dbfs:/home/cluster_log\" } }`", + "description": "", "properties": { "destination": { - "description": "dbfs destination, e.g. `dbfs:/my/path`" + "description": "" } } }, "file": { - "description": "destination needs to be provided. e.g.\n`{ \"file\" : { \"destination\" : \"file:/my/local/file.sh\" } }`", + "description": "", "properties": { "destination": { - "description": "local file destination, e.g. `file:/my/local/file.sh`" + "description": "" } } }, "s3": { - "description": "destination and either the region or endpoint need to be provided. e.g.\n`{ \"s3\": { \"destination\" : \"s3://cluster_log_bucket/prefix\", \"region\" : \"us-west-2\" } }`\nCluster iam role is used to access s3, please make sure the cluster iam role in\n`instance_profile_arn` has permission to write data to the s3 destination.", + "description": "", "properties": { "canned_acl": { - "description": "(Optional) Set canned access control list for the logs, e.g. `bucket-owner-full-control`.\nIf `canned_cal` is set, please make sure the cluster iam role has `s3:PutObjectAcl` permission on\nthe destination bucket and prefix. The full list of possible canned acl can be found at\nhttp://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl.\nPlease also note that by default only the object owner gets full controls. If you are using cross account\nrole for writing data, you may want to set `bucket-owner-full-control` to make bucket owner able to\nread the logs." + "description": "" }, "destination": { - "description": "S3 destination, e.g. `s3://my-bucket/some-prefix` Note that logs will be delivered using\ncluster iam role, please make sure you set cluster iam role and the role has write access to the\ndestination. Please also note that you cannot use AWS keys to deliver logs." + "description": "" }, "enable_encryption": { - "description": "(Optional) Flag to enable server side encryption, `false` by default." + "description": "" }, "encryption_type": { - "description": "(Optional) The encryption type, it could be `sse-s3` or `sse-kms`. It will be used only when\nencryption is enabled and the default type is `sse-s3`." + "description": "" }, "endpoint": { - "description": "S3 endpoint, e.g. `https://s3-us-west-2.amazonaws.com`. Either region or endpoint needs to be set.\nIf both are set, endpoint will be used." + "description": "" }, "kms_key": { - "description": "(Optional) Kms key which will be used if encryption is enabled and encryption type is set to `sse-kms`." + "description": "" }, "region": { - "description": "S3 region, e.g. `us-west-2`. Either region or endpoint needs to be set. If both are set,\nendpoint will be used." + "description": "" } } }, "volumes": { - "description": "destination needs to be provided. e.g.\n`{ \"volumes\" : { \"destination\" : \"/Volumes/my-init.sh\" } }`", + "description": "", "properties": { "destination": { - "description": "Unity Catalog Volumes file destination, e.g. `/Volumes/my-init.sh`" + "description": "" } } }, "workspace": { - "description": "destination needs to be provided. e.g.\n`{ \"workspace\" : { \"destination\" : \"/Users/user1@databricks.com/my-init.sh\" } }`", + "description": "", "properties": { "destination": { - "description": "workspace files destination, e.g. `/Users/user1@databricks.com/my-init.sh`" + "description": "" } } } diff --git a/cmd/workspace/cluster-policies/cluster-policies.go b/cmd/workspace/cluster-policies/cluster-policies.go index bfe50523655..208bc9617c0 100755 --- a/cmd/workspace/cluster-policies/cluster-policies.go +++ b/cmd/workspace/cluster-policies/cluster-policies.go @@ -26,11 +26,10 @@ func New() *cobra.Command { limit their use to specific users and groups. With cluster policies, you can: - Auto-install cluster libraries on the next - restart by listing them in the policy's "libraries" field (Public Preview). - - Limit users to creating clusters with the prescribed settings. - Simplify the - user interface, enabling more users to create clusters, by fixing and hiding - some fields. - Manage costs by setting limits on attributes that impact the - hourly rate. + restart by listing them in the policy's "libraries" field. - Limit users to + creating clusters with the prescribed settings. - Simplify the user interface, + enabling more users to create clusters, by fixing and hiding some fields. - + Manage costs by setting limits on attributes that impact the hourly rate. Cluster policy permissions limit which policies a user can select in the Policy drop-down when the user creates a cluster: - A user who has diff --git a/cmd/workspace/clusters/clusters.go b/cmd/workspace/clusters/clusters.go index 9f49beb5990..b89654af3f1 100755 --- a/cmd/workspace/clusters/clusters.go +++ b/cmd/workspace/clusters/clusters.go @@ -84,9 +84,8 @@ func newChangeOwner() *cobra.Command { cmd.Short = `Change cluster owner.` cmd.Long = `Change cluster owner. - Change the owner of the cluster. You must be an admin and the cluster must be - terminated to perform this operation. The service principal application ID can - be supplied as an argument to owner_username. + Change the owner of the cluster. You must be an admin to perform this + operation. Arguments: CLUSTER_ID: diff --git a/cmd/workspace/cmd.go b/cmd/workspace/cmd.go index 47ad795e634..f8e911d1f14 100755 --- a/cmd/workspace/cmd.go +++ b/cmd/workspace/cmd.go @@ -27,7 +27,6 @@ import ( instance_profiles "github.com/databricks/cli/cmd/workspace/instance-profiles" ip_access_lists "github.com/databricks/cli/cmd/workspace/ip-access-lists" jobs "github.com/databricks/cli/cmd/workspace/jobs" - lakehouse_monitors "github.com/databricks/cli/cmd/workspace/lakehouse-monitors" lakeview "github.com/databricks/cli/cmd/workspace/lakeview" libraries "github.com/databricks/cli/cmd/workspace/libraries" metastores "github.com/databricks/cli/cmd/workspace/metastores" @@ -94,7 +93,6 @@ func All() []*cobra.Command { out = append(out, instance_profiles.New()) out = append(out, ip_access_lists.New()) out = append(out, jobs.New()) - out = append(out, lakehouse_monitors.New()) out = append(out, lakeview.New()) out = append(out, libraries.New()) out = append(out, metastores.New()) diff --git a/cmd/workspace/dashboards/dashboards.go b/cmd/workspace/dashboards/dashboards.go index 3b21a874836..24faf3b6129 100755 --- a/cmd/workspace/dashboards/dashboards.go +++ b/cmd/workspace/dashboards/dashboards.go @@ -122,7 +122,7 @@ func newDelete() *cobra.Command { cmd.Use = "delete DASHBOARD_ID" cmd.Short = `Remove a dashboard.` cmd.Long = `Remove a dashboard. - + Moves a dashboard to the trash. Trashed dashboards do not appear in list views or searches, and cannot be shared.` @@ -196,7 +196,7 @@ func newGet() *cobra.Command { cmd.Use = "get DASHBOARD_ID" cmd.Short = `Retrieve a definition.` cmd.Long = `Retrieve a definition. - + Returns a JSON representation of a dashboard object, including its visualization and query objects.` @@ -275,7 +275,7 @@ func newList() *cobra.Command { cmd.Use = "list" cmd.Short = `Get dashboard objects.` cmd.Long = `Get dashboard objects. - + Fetch a paginated list of dashboard objects.` cmd.Annotations = make(map[string]string) @@ -331,7 +331,7 @@ func newRestore() *cobra.Command { cmd.Use = "restore DASHBOARD_ID" cmd.Short = `Restore a dashboard.` cmd.Long = `Restore a dashboard. - + A restored dashboard appears in list views and searches and can be shared.` cmd.Annotations = make(map[string]string) @@ -385,91 +385,4 @@ func init() { }) } -// start update command - -// Slice with functions to override default command behavior. -// Functions can be added from the `init()` function in manually curated files in this directory. -var updateOverrides []func( - *cobra.Command, - *sql.DashboardEditContent, -) - -func newUpdate() *cobra.Command { - cmd := &cobra.Command{} - - var updateReq sql.DashboardEditContent - var updateJson flags.JsonFlag - - // TODO: short flags - cmd.Flags().Var(&updateJson, "json", `either inline JSON string or @path/to/file.json with request body`) - - cmd.Flags().StringVar(&updateReq.Name, "name", updateReq.Name, `The title of this dashboard that appears in list views and at the top of the dashboard page.`) - cmd.Flags().Var(&updateReq.RunAsRole, "run-as-role", `Sets the **Run as** role for the object. Supported values: [owner, viewer]`) - - cmd.Use = "update DASHBOARD_ID" - cmd.Short = `Change a dashboard definition.` - cmd.Long = `Change a dashboard definition. - - Modify this dashboard definition. This operation only affects attributes of - the dashboard object. It does not add, modify, or remove widgets. - - **Note**: You cannot undo this operation.` - - cmd.Annotations = make(map[string]string) - - cmd.PreRunE = root.MustWorkspaceClient - cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { - ctx := cmd.Context() - w := root.WorkspaceClient(ctx) - - if cmd.Flags().Changed("json") { - err = updateJson.Unmarshal(&updateReq) - if err != nil { - return err - } - } - if len(args) == 0 { - promptSpinner := cmdio.Spinner(ctx) - promptSpinner <- "No DASHBOARD_ID argument specified. Loading names for Dashboards drop-down." - names, err := w.Dashboards.DashboardNameToIdMap(ctx, sql.ListDashboardsRequest{}) - close(promptSpinner) - if err != nil { - return fmt.Errorf("failed to load names for Dashboards drop-down. Please manually specify required arguments. Original error: %w", err) - } - id, err := cmdio.Select(ctx, names, "") - if err != nil { - return err - } - args = append(args, id) - } - if len(args) != 1 { - return fmt.Errorf("expected to have ") - } - updateReq.DashboardId = args[0] - - response, err := w.Dashboards.Update(ctx, updateReq) - if err != nil { - return err - } - return cmdio.Render(ctx, response) - } - - // Disable completions since they are not applicable. - // Can be overridden by manual implementation in `override.go`. - cmd.ValidArgsFunction = cobra.NoFileCompletions - - // Apply optional overrides to this command. - for _, fn := range updateOverrides { - fn(cmd, &updateReq) - } - - return cmd -} - -func init() { - cmdOverrides = append(cmdOverrides, func(cmd *cobra.Command) { - cmd.AddCommand(newUpdate()) - }) -} - // end service Dashboards diff --git a/cmd/workspace/experiments/experiments.go b/cmd/workspace/experiments/experiments.go index 653e8fe242e..380a03838e7 100755 --- a/cmd/workspace/experiments/experiments.go +++ b/cmd/workspace/experiments/experiments.go @@ -397,9 +397,7 @@ func newDeleteRuns() *cobra.Command { cmd.Long = `Delete runs by creation time. Bulk delete runs in an experiment that were created prior to or at the - specified timestamp. Deletes at most max_runs per request. To call this API - from a Databricks Notebook in Python, you can use the client code snippet on - https://learn.microsoft.com/en-us/azure/databricks/mlflow/runs#bulk-delete. + specified timestamp. Deletes at most max_runs per request. Arguments: EXPERIMENT_ID: The ID of the experiment containing the runs to delete. @@ -1714,9 +1712,7 @@ func newRestoreRuns() *cobra.Command { cmd.Long = `Restore runs by deletion time. Bulk restore runs in an experiment that were deleted no earlier than the - specified timestamp. Restores at most max_runs per request. To call this API - from a Databricks Notebook in Python, you can use the client code snippet on - https://learn.microsoft.com/en-us/azure/databricks/mlflow/runs#bulk-restore. + specified timestamp. Restores at most max_runs per request. Arguments: EXPERIMENT_ID: The ID of the experiment containing the runs to restore. diff --git a/cmd/workspace/global-init-scripts/global-init-scripts.go b/cmd/workspace/global-init-scripts/global-init-scripts.go index 389ef86d0a3..8eebdd5737e 100755 --- a/cmd/workspace/global-init-scripts/global-init-scripts.go +++ b/cmd/workspace/global-init-scripts/global-init-scripts.go @@ -301,7 +301,7 @@ func newList() *cobra.Command { Get a list of all global init scripts for this workspace. This returns all properties for each script but **not** the script contents. To retrieve the contents of a script, use the [get a global init - script](:method:globalinitscripts/get) operation.` + script](#operation/get-script) operation.` cmd.Annotations = make(map[string]string) diff --git a/cmd/workspace/pipelines/pipelines.go b/cmd/workspace/pipelines/pipelines.go index 8cff417e96a..dc5ef5e75ed 100755 --- a/cmd/workspace/pipelines/pipelines.go +++ b/cmd/workspace/pipelines/pipelines.go @@ -905,7 +905,6 @@ func newStartUpdate() *cobra.Command { cmd.Flags().BoolVar(&startUpdateReq.FullRefresh, "full-refresh", startUpdateReq.FullRefresh, `If true, this update will reset all tables before running.`) // TODO: array: full_refresh_selection // TODO: array: refresh_selection - cmd.Flags().BoolVar(&startUpdateReq.ValidateOnly, "validate-only", startUpdateReq.ValidateOnly, `If true, this update only validates the correctness of pipeline source code but does not materialize or publish any datasets.`) cmd.Use = "start-update PIPELINE_ID" cmd.Short = `Start a pipeline.` diff --git a/cmd/workspace/queries/queries.go b/cmd/workspace/queries/queries.go index ccb532ec756..1f165218665 100755 --- a/cmd/workspace/queries/queries.go +++ b/cmd/workspace/queries/queries.go @@ -286,10 +286,7 @@ func newList() *cobra.Command { cmd.Long = `Get a list of queries. Gets a list of queries. Optionally, this list can be filtered by a search - term. - - ### **Warning: Calling this API concurrently 10 or more times could result in - throttling, service degradation, or a temporary ban.**` + term.` cmd.Annotations = make(map[string]string) @@ -422,7 +419,6 @@ func newUpdate() *cobra.Command { cmd.Flags().StringVar(&updateReq.Name, "name", updateReq.Name, `The title of this query that appears in list views, widget headings, and on the query page.`) // TODO: any: options cmd.Flags().StringVar(&updateReq.Query, "query", updateReq.Query, `The text of the query to be run.`) - cmd.Flags().Var(&updateReq.RunAsRole, "run-as-role", `Sets the **Run as** role for the object. Supported values: [owner, viewer]`) cmd.Use = "update QUERY_ID" cmd.Short = `Change a query definition.` diff --git a/cmd/workspace/tables/tables.go b/cmd/workspace/tables/tables.go index a840fd23363..7fcee015bc1 100755 --- a/cmd/workspace/tables/tables.go +++ b/cmd/workspace/tables/tables.go @@ -123,89 +123,6 @@ func init() { }) } -// start exists command - -// Slice with functions to override default command behavior. -// Functions can be added from the `init()` function in manually curated files in this directory. -var existsOverrides []func( - *cobra.Command, - *catalog.ExistsRequest, -) - -func newExists() *cobra.Command { - cmd := &cobra.Command{} - - var existsReq catalog.ExistsRequest - - // TODO: short flags - - cmd.Use = "exists FULL_NAME" - cmd.Short = `Get boolean reflecting if table exists.` - cmd.Long = `Get boolean reflecting if table exists. - - Gets if a table exists in the metastore for a specific catalog and schema. The - caller must satisfy one of the following requirements: * Be a metastore admin - * Be the owner of the parent catalog * Be the owner of the parent schema and - have the USE_CATALOG privilege on the parent catalog * Have the - **USE_CATALOG** privilege on the parent catalog and the **USE_SCHEMA** - privilege on the parent schema, and either be the table owner or have the - SELECT privilege on the table. * Have BROWSE privilege on the parent catalog * - Have BROWSE privilege on the parent schema. - - Arguments: - FULL_NAME: Full name of the table.` - - cmd.Annotations = make(map[string]string) - - cmd.PreRunE = root.MustWorkspaceClient - cmd.RunE = func(cmd *cobra.Command, args []string) (err error) { - ctx := cmd.Context() - w := root.WorkspaceClient(ctx) - - if len(args) == 0 { - promptSpinner := cmdio.Spinner(ctx) - promptSpinner <- "No FULL_NAME argument specified. Loading names for Tables drop-down." - names, err := w.Tables.TableInfoNameToTableIdMap(ctx, catalog.ListTablesRequest{}) - close(promptSpinner) - if err != nil { - return fmt.Errorf("failed to load names for Tables drop-down. Please manually specify required arguments. Original error: %w", err) - } - id, err := cmdio.Select(ctx, names, "Full name of the table") - if err != nil { - return err - } - args = append(args, id) - } - if len(args) != 1 { - return fmt.Errorf("expected to have full name of the table") - } - existsReq.FullName = args[0] - - response, err := w.Tables.Exists(ctx, existsReq) - if err != nil { - return err - } - return cmdio.Render(ctx, response) - } - - // Disable completions since they are not applicable. - // Can be overridden by manual implementation in `override.go`. - cmd.ValidArgsFunction = cobra.NoFileCompletions - - // Apply optional overrides to this command. - for _, fn := range existsOverrides { - fn(cmd, &existsReq) - } - - return cmd -} - -func init() { - cmdOverrides = append(cmdOverrides, func(cmd *cobra.Command) { - cmd.AddCommand(newExists()) - }) -} - // start get command // Slice with functions to override default command behavior. @@ -229,12 +146,10 @@ func newGet() *cobra.Command { cmd.Long = `Get a table. Gets a table from the metastore for a specific catalog and schema. The caller - must satisfy one of the following requirements: * Be a metastore admin * Be - the owner of the parent catalog * Be the owner of the parent schema and have - the USE_CATALOG privilege on the parent catalog * Have the **USE_CATALOG** - privilege on the parent catalog and the **USE_SCHEMA** privilege on the parent - schema, and either be the table owner or have the SELECT privilege on the - table. + must be a metastore admin, be the owner of the table and have the + **USE_CATALOG** privilege on the parent catalog and the **USE_SCHEMA** + privilege on the parent schema, or be the owner of the table and have the + **SELECT** privilege on it as well. Arguments: FULL_NAME: Full name of the table.` diff --git a/cmd/workspace/token-management/token-management.go b/cmd/workspace/token-management/token-management.go index c013181fe64..bfd28f29a8e 100755 --- a/cmd/workspace/token-management/token-management.go +++ b/cmd/workspace/token-management/token-management.go @@ -56,16 +56,16 @@ func newCreateOboToken() *cobra.Command { cmd.Flags().Var(&createOboTokenJson, "json", `either inline JSON string or @path/to/file.json with request body`) cmd.Flags().StringVar(&createOboTokenReq.Comment, "comment", createOboTokenReq.Comment, `Comment that describes the purpose of the token.`) - cmd.Flags().Int64Var(&createOboTokenReq.LifetimeSeconds, "lifetime-seconds", createOboTokenReq.LifetimeSeconds, `The number of seconds before the token expires.`) - cmd.Use = "create-obo-token APPLICATION_ID" + cmd.Use = "create-obo-token APPLICATION_ID LIFETIME_SECONDS" cmd.Short = `Create on-behalf token.` cmd.Long = `Create on-behalf token. - + Creates a token on behalf of a service principal. Arguments: - APPLICATION_ID: Application ID of the service principal.` + APPLICATION_ID: Application ID of the service principal. + LIFETIME_SECONDS: The number of seconds before the token expires.` cmd.Annotations = make(map[string]string) @@ -73,11 +73,12 @@ func newCreateOboToken() *cobra.Command { if cmd.Flags().Changed("json") { err := cobra.ExactArgs(0)(cmd, args) if err != nil { - return fmt.Errorf("when --json flag is specified, no positional arguments are required. Provide 'application_id' in your JSON input") + return fmt.Errorf("when --json flag is specified, no positional arguments are required. Provide 'application_id', 'lifetime_seconds' in your JSON input") } return nil } - return nil + check := cobra.ExactArgs(2) + return check(cmd, args) } cmd.PreRunE = root.MustWorkspaceClient @@ -90,26 +91,16 @@ func newCreateOboToken() *cobra.Command { if err != nil { return err } - } else { - if len(args) == 0 { - promptSpinner := cmdio.Spinner(ctx) - promptSpinner <- "No APPLICATION_ID argument specified. Loading names for Token Management drop-down." - names, err := w.TokenManagement.TokenInfoCommentToTokenIdMap(ctx, settings.ListTokenManagementRequest{}) - close(promptSpinner) - if err != nil { - return fmt.Errorf("failed to load names for Token Management drop-down. Please manually specify required arguments. Original error: %w", err) - } - id, err := cmdio.Select(ctx, names, "Application ID of the service principal") - if err != nil { - return err - } - args = append(args, id) - } - if len(args) != 1 { - return fmt.Errorf("expected to have application id of the service principal") - } + } + if !cmd.Flags().Changed("json") { createOboTokenReq.ApplicationId = args[0] } + if !cmd.Flags().Changed("json") { + _, err = fmt.Sscan(args[1], &createOboTokenReq.LifetimeSeconds) + if err != nil { + return fmt.Errorf("invalid LIFETIME_SECONDS: %s", args[1]) + } + } response, err := w.TokenManagement.CreateOboToken(ctx, createOboTokenReq) if err != nil { @@ -155,7 +146,7 @@ func newDelete() *cobra.Command { cmd.Use = "delete TOKEN_ID" cmd.Short = `Delete a token.` cmd.Long = `Delete a token. - + Deletes a token, specified by its ID. Arguments: @@ -231,7 +222,7 @@ func newGet() *cobra.Command { cmd.Use = "get TOKEN_ID" cmd.Short = `Get token info.` cmd.Long = `Get token info. - + Gets information about a token, specified by its ID. Arguments: @@ -302,7 +293,7 @@ func newGetPermissionLevels() *cobra.Command { cmd.Use = "get-permission-levels" cmd.Short = `Get token permission levels.` cmd.Long = `Get token permission levels. - + Gets the permission levels that a user can have on an object.` cmd.Annotations = make(map[string]string) @@ -350,7 +341,7 @@ func newGetPermissions() *cobra.Command { cmd.Use = "get-permissions" cmd.Short = `Get token permissions.` cmd.Long = `Get token permissions. - + Gets the permissions of all tokens. Tokens can inherit permissions from their root object.` @@ -407,7 +398,7 @@ func newList() *cobra.Command { cmd.Use = "list" cmd.Short = `List all tokens.` cmd.Long = `List all tokens. - + Lists all tokens associated with the specified workspace or user.` cmd.Annotations = make(map[string]string) @@ -467,7 +458,7 @@ func newSetPermissions() *cobra.Command { cmd.Use = "set-permissions" cmd.Short = `Set token permissions.` cmd.Long = `Set token permissions. - + Sets permissions on all tokens. Tokens can inherit permissions from their root object.` @@ -538,7 +529,7 @@ func newUpdatePermissions() *cobra.Command { cmd.Use = "update-permissions" cmd.Short = `Update token permissions.` cmd.Long = `Update token permissions. - + Updates the permissions on all tokens. Tokens can inherit permissions from their root object.` From 21a9bb3010f0bd34ee36a1731bf750f84fa3c42f Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Tue, 13 Feb 2024 06:15:15 -0800 Subject: [PATCH 08/13] address comments --- cmd/fs/cat.go | 2 +- libs/cmdio/render.go | 41 +++++++++++++++-------------------------- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/cmd/fs/cat.go b/cmd/fs/cat.go index 8227cd78141..90cf940809b 100644 --- a/cmd/fs/cat.go +++ b/cmd/fs/cat.go @@ -27,7 +27,7 @@ func newCatCommand() *cobra.Command { if err != nil { return err } - return cmdio.RenderReader(ctx, r) + return cmdio.Render(ctx, r) } return cmd diff --git a/libs/cmdio/render.go b/libs/cmdio/render.go index 42c7317e4df..b42b0577aac 100644 --- a/libs/cmdio/render.go +++ b/libs/cmdio/render.go @@ -68,8 +68,8 @@ type textRenderer interface { } type templateRenderer interface { - // Render an object using the provided template and write to the provided writeFlusher. - renderTemplate(context.Context, *template.Template, writeFlusher) error + // Render an object using the provided template and write to the provided tabwriter.Writer. + renderTemplate(context.Context, *template.Template, *tabwriter.Writer) error } type readerRenderer struct { @@ -135,7 +135,7 @@ func (ir iteratorRenderer) renderJson(ctx context.Context, w writeFlusher) error return w.Flush() } -func (ir iteratorRenderer) renderTemplate(ctx context.Context, t *template.Template, w writeFlusher) error { +func (ir iteratorRenderer) renderTemplate(ctx context.Context, t *template.Template, w *tabwriter.Writer) error { buf := make([]any, 0, ir.getBufferSize()) for i := 0; ir.t.HasNext(ctx); i++ { n, err := ir.t.Next(ctx) @@ -184,7 +184,7 @@ func (d defaultRenderer) renderJson(_ context.Context, w writeFlusher) error { return nil } -func (d defaultRenderer) renderTemplate(_ context.Context, t *template.Template, w writeFlusher) error { +func (d defaultRenderer) renderTemplate(_ context.Context, t *template.Template, w *tabwriter.Writer) error { return t.Execute(w, d.it) } @@ -224,54 +224,43 @@ func newBufferedFlusher(w io.Writer) writeFlusher { return bufferedFlusher{w: w} } -func renderWithTemplate(r any, ctx context.Context, headerTemplate, template string) error { +func renderWithTemplate(r any, ctx context.Context, outputFormat flags.Output, w io.Writer, headerTemplate, template string) error { // TODO: add terminal width & white/dark theme detection - c := fromContext(ctx) - switch c.outputFormat { + switch outputFormat { case flags.OutputJSON: if jr, ok := r.(jsonRenderer); ok { - return jr.renderJson(ctx, newBufferedFlusher(c.out)) + return jr.renderJson(ctx, newBufferedFlusher(w)) } return errors.New("json output not supported") case flags.OutputText: if tr, ok := r.(templateRenderer); ok && template != "" { - return renderUsingTemplate(ctx, tr, c.out, headerTemplate, template) + return renderUsingTemplate(ctx, tr, w, headerTemplate, template) } if tr, ok := r.(textRenderer); ok { - return tr.renderText(ctx, newBufferedFlusher(c.out)) + return tr.renderText(ctx, newBufferedFlusher(w)) } if jr, ok := r.(jsonRenderer); ok { - return jr.renderJson(ctx, newBufferedFlusher(c.out)) + return jr.renderJson(ctx, newBufferedFlusher(w)) } return errors.New("no renderer defined") default: - return fmt.Errorf("invalid output format: %s", c.outputFormat) + return fmt.Errorf("invalid output format: %s", outputFormat) } } func Render(ctx context.Context, v any) error { c := fromContext(ctx) - return renderWithTemplate(newRenderer(v), ctx, c.headerTemplate, c.template) + return renderWithTemplate(newRenderer(v), ctx, c.outputFormat, c.out, c.headerTemplate, c.template) } func RenderWithTemplate(ctx context.Context, v any, headerTemplate, template string) error { - return renderWithTemplate(newRenderer(v), ctx, headerTemplate, template) -} - -func RenderJson(ctx context.Context, v any) error { c := fromContext(ctx) - if jr, ok := newRenderer(v).(jsonRenderer); ok { - return jr.renderJson(ctx, newBufferedFlusher(c.out)) - } - return errors.New("json output not supported") + return renderWithTemplate(newRenderer(v), ctx, c.outputFormat, c.out, headerTemplate, template) } -func RenderReader(ctx context.Context, r io.Reader) error { +func RenderJson(ctx context.Context, v any) error { c := fromContext(ctx) - if jr, ok := newRenderer(r).(textRenderer); ok { - return jr.renderText(ctx, newBufferedFlusher(c.out)) - } - return errors.New("rendering io.Reader not supported") + return renderWithTemplate(newRenderer(v), ctx, flags.OutputJSON, c.out, c.headerTemplate, c.template) } func renderUsingTemplate(ctx context.Context, r templateRenderer, w io.Writer, headerTmpl, tmpl string) error { From 2a2e188294915b83afb2ca7c5a70031235c9cc7a Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Mon, 19 Feb 2024 11:41:11 +0100 Subject: [PATCH 09/13] address some concerns --- libs/cmdio/render.go | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/libs/cmdio/render.go b/libs/cmdio/render.go index b42b0577aac..125675e884c 100644 --- a/libs/cmdio/render.go +++ b/libs/cmdio/render.go @@ -64,7 +64,7 @@ type jsonRenderer interface { type textRenderer interface { // Render an object as text to the provided writeFlusher. - renderText(context.Context, writeFlusher) error + renderText(context.Context, io.Writer) error } type templateRenderer interface { @@ -76,12 +76,9 @@ type readerRenderer struct { reader io.Reader } -func (r readerRenderer) renderText(_ context.Context, w writeFlusher) error { +func (r readerRenderer) renderText(_ context.Context, w io.Writer) error { _, err := io.Copy(w, r.reader) - if err != nil { - return err - } - return w.Flush() + return err } type iteratorRenderer struct { @@ -152,7 +149,7 @@ func (ir iteratorRenderer) renderTemplate(ctx context.Context, t *template.Templ if err != nil { return err } - buf = make([]any, 0, ir.getBufferSize()) + buf = buf[:0] } } if len(buf) > 0 { @@ -165,11 +162,11 @@ func (ir iteratorRenderer) renderTemplate(ctx context.Context, t *template.Templ } type defaultRenderer struct { - it any + t any } func (d defaultRenderer) renderJson(_ context.Context, w writeFlusher) error { - pretty, err := fancyJSON(d.it) + pretty, err := fancyJSON(d.t) if err != nil { return err } @@ -185,21 +182,21 @@ func (d defaultRenderer) renderJson(_ context.Context, w writeFlusher) error { } func (d defaultRenderer) renderTemplate(_ context.Context, t *template.Template, w *tabwriter.Writer) error { - return t.Execute(w, d.it) + return t.Execute(w, d.t) } // Returns something implementing one of the following interfaces: // - jsonRenderer // - textRenderer // - templateRenderer -func newRenderer(it any) any { - if r, ok := it.(io.Reader); ok { +func newRenderer(t any) any { + if r, ok := t.(io.Reader); ok { return readerRenderer{reader: r} } - if iterator, ok := newReflectIterator(it); ok { + if iterator, ok := newReflectIterator(t); ok { return iteratorRenderer{t: iterator} } - return defaultRenderer{it: it} + return defaultRenderer{t: t} } type bufferedFlusher struct { @@ -237,7 +234,7 @@ func renderWithTemplate(r any, ctx context.Context, outputFormat flags.Output, w return renderUsingTemplate(ctx, tr, w, headerTemplate, template) } if tr, ok := r.(textRenderer); ok { - return tr.renderText(ctx, newBufferedFlusher(w)) + return tr.renderText(ctx, w) } if jr, ok := r.(jsonRenderer); ok { return jr.renderJson(ctx, newBufferedFlusher(w)) From cc3dc5b31d781f68f1dc3568220e8094624c6da4 Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Mon, 19 Feb 2024 13:10:36 +0100 Subject: [PATCH 10/13] buffer --- libs/cmdio/render.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/libs/cmdio/render.go b/libs/cmdio/render.go index 125675e884c..e2a6dac0796 100644 --- a/libs/cmdio/render.go +++ b/libs/cmdio/render.go @@ -178,7 +178,7 @@ func (d defaultRenderer) renderJson(_ context.Context, w writeFlusher) error { if err != nil { return err } - return nil + return w.Flush() } func (d defaultRenderer) renderTemplate(_ context.Context, t *template.Template, w *tabwriter.Writer) error { @@ -201,7 +201,7 @@ func newRenderer(t any) any { type bufferedFlusher struct { w io.Writer - b bytes.Buffer + b *bytes.Buffer } func (b bufferedFlusher) Write(bs []byte) (int, error) { @@ -209,7 +209,7 @@ func (b bufferedFlusher) Write(bs []byte) (int, error) { } func (b bufferedFlusher) Flush() error { - _, err := b.Write(b.b.Bytes()) + _, err := b.w.Write(b.b.Bytes()) if err != nil { return err } @@ -218,7 +218,10 @@ func (b bufferedFlusher) Flush() error { } func newBufferedFlusher(w io.Writer) writeFlusher { - return bufferedFlusher{w: w} + return bufferedFlusher{ + w: w, + b: &bytes.Buffer{}, + } } func renderWithTemplate(r any, ctx context.Context, outputFormat flags.Output, w io.Writer, headerTemplate, template string) error { From 480cf92f3f884ea7e337d44342b1a4f88a04dd3a Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Mon, 19 Feb 2024 13:27:52 +0100 Subject: [PATCH 11/13] smaller buffer --- libs/cmdio/render.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/cmdio/render.go b/libs/cmdio/render.go index e2a6dac0796..3438a833bfd 100644 --- a/libs/cmdio/render.go +++ b/libs/cmdio/render.go @@ -88,7 +88,7 @@ type iteratorRenderer struct { func (ir iteratorRenderer) getBufferSize() int { if ir.bufferSize == 0 { - return 100 + return 20 } return ir.bufferSize } From 542ae6464b1b09380d6501d6befd9a654ad90830 Mon Sep 17 00:00:00 2001 From: Andrew Nester Date: Tue, 20 Feb 2024 17:29:53 +0100 Subject: [PATCH 12/13] do not use reflect --- .codegen/service.go.tmpl | 4 +- bundle/schema/docs/bundle_descriptions.json | 70 +++++++++++++++---- cmd/account/budgets/budgets.go | 2 +- .../custom-app-integration.go | 2 +- cmd/account/groups/groups.go | 2 +- .../ip-access-lists/ip-access-lists.go | 2 +- cmd/account/log-delivery/log-delivery.go | 2 +- .../metastore-assignments.go | 2 +- cmd/account/metastores/metastores.go | 2 +- .../network-connectivity.go | 4 +- .../o-auth-published-apps.go | 2 +- .../published-app-integration.go | 2 +- .../service-principal-secrets.go | 2 +- .../service-principals/service-principals.go | 2 +- cmd/account/users/users.go | 2 +- .../workspace-assignment.go | 2 +- cmd/workspace/catalogs/catalogs.go | 2 +- cmd/workspace/clean-rooms/clean-rooms.go | 2 +- .../cluster-policies/cluster-policies.go | 2 +- cmd/workspace/clusters/clusters.go | 4 +- cmd/workspace/connections/connections.go | 2 +- cmd/workspace/dashboards/dashboards.go | 2 +- cmd/workspace/experiments/experiments.go | 10 +-- .../external-locations/external-locations.go | 2 +- cmd/workspace/functions/functions.go | 2 +- .../git-credentials/git-credentials.go | 2 +- .../global-init-scripts.go | 2 +- cmd/workspace/groups/groups.go | 2 +- .../instance-pools/instance-pools.go | 2 +- .../instance-profiles/instance-profiles.go | 2 +- .../ip-access-lists/ip-access-lists.go | 2 +- cmd/workspace/jobs/jobs.go | 4 +- cmd/workspace/libraries/libraries.go | 2 +- cmd/workspace/metastores/metastores.go | 2 +- .../model-registry/model-registry.go | 12 ++-- .../model-versions/model-versions.go | 2 +- cmd/workspace/pipelines/pipelines.go | 4 +- .../policy-families/policy-families.go | 2 +- cmd/workspace/providers/providers.go | 4 +- cmd/workspace/queries/queries.go | 2 +- cmd/workspace/query-history/query-history.go | 2 +- cmd/workspace/recipients/recipients.go | 2 +- .../registered-models/registered-models.go | 2 +- cmd/workspace/repos/repos.go | 2 +- cmd/workspace/schemas/schemas.go | 2 +- cmd/workspace/secrets/secrets.go | 6 +- .../service-principals/service-principals.go | 2 +- .../serving-endpoints/serving-endpoints.go | 2 +- cmd/workspace/shares/shares.go | 2 +- .../storage-credentials.go | 2 +- .../system-schemas/system-schemas.go | 2 +- cmd/workspace/tables/tables.go | 4 +- .../token-management/token-management.go | 2 +- cmd/workspace/tokens/tokens.go | 2 +- cmd/workspace/users/users.go | 2 +- .../vector-search-endpoints.go | 2 +- .../vector-search-indexes.go | 2 +- cmd/workspace/volumes/volumes.go | 2 +- cmd/workspace/warehouses/warehouses.go | 2 +- cmd/workspace/workspace/workspace.go | 2 +- libs/cmdio/reflect_iterator.go | 45 ------------ libs/cmdio/reflect_iterator_test.go | 50 ------------- libs/cmdio/render.go | 46 +++++++++--- libs/cmdio/render_test.go | 39 ++++++++--- 64 files changed, 203 insertions(+), 201 deletions(-) delete mode 100644 libs/cmdio/reflect_iterator.go delete mode 100644 libs/cmdio/reflect_iterator_test.go diff --git a/.codegen/service.go.tmpl b/.codegen/service.go.tmpl index f1d1ca5aca2..ad25135aeb7 100644 --- a/.codegen/service.go.tmpl +++ b/.codegen/service.go.tmpl @@ -313,9 +313,9 @@ func init() { {{ if .Response -}} {{- if .IsResponseByteStream -}} defer response.{{.ResponseBodyField.PascalName}}.Close() - return cmdio.Render(ctx, response.{{.ResponseBodyField.PascalName}}) + return cmdio.Render{{ if .Pagination}}Iterator{{end}}(ctx, response.{{.ResponseBodyField.PascalName}}) {{- else -}} - return cmdio.Render(ctx, response) + return cmdio.Render{{ if .Pagination}}Iterator{{end}}(ctx, response) {{- end -}} {{ else -}} return nil diff --git a/bundle/schema/docs/bundle_descriptions.json b/bundle/schema/docs/bundle_descriptions.json index 2501b7aad0a..98a54fbd8c2 100644 --- a/bundle/schema/docs/bundle_descriptions.json +++ b/bundle/schema/docs/bundle_descriptions.json @@ -193,7 +193,7 @@ "description": "An optional continuous property for this job. The continuous property will ensure that there is always one run executing. Only one of `schedule` and `continuous` can be used.", "properties": { "pause_status": { - "description": "Whether this trigger is paused or not." + "description": "Indicate whether this schedule is paused or not." } } }, @@ -725,7 +725,7 @@ "description": "An optional periodic schedule for this job. The default behavior is that the job only runs when triggered by clicking “Run Now” in the Jobs UI or sending an API request to `runNow`.", "properties": { "pause_status": { - "description": "Whether this trigger is paused or not." + "description": "Indicate whether this schedule is paused or not." }, "quartz_cron_expression": { "description": "A Cron expression using Quartz syntax that describes the schedule for a job.\nSee [Cron Trigger](http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html)\nfor details. This field is required.\"\n" @@ -785,7 +785,7 @@ "description": "Optional schema to write to. This parameter is only used when a warehouse_id is also provided. If not provided, the `default` schema is used." }, "source": { - "description": "Optional location type of the notebook. When set to `WORKSPACE`, the notebook will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the notebook will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Notebook is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Notebook is located in cloud Git provider.\n" + "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" }, "warehouse_id": { "description": "ID of the SQL warehouse to connect to. If provided, we automatically generate and provide the profile and connection details to dbt. It can be overridden on a per-command basis by using the `--profiles-dir` command line argument." @@ -1449,7 +1449,7 @@ "description": "Path of the SQL file. Must be relative if the source is a remote Git repository and absolute for workspace paths." }, "source": { - "description": "Optional location type of the notebook. When set to `WORKSPACE`, the notebook will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the notebook will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Notebook is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Notebook is located in cloud Git provider.\n" + "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" } } }, @@ -1551,7 +1551,7 @@ } }, "pause_status": { - "description": "Whether this trigger is paused or not." + "description": "Indicate whether this schedule is paused or not." }, "table": { "description": "Table trigger settings.", @@ -2725,7 +2725,7 @@ "description": "An optional continuous property for this job. The continuous property will ensure that there is always one run executing. Only one of `schedule` and `continuous` can be used.", "properties": { "pause_status": { - "description": "Whether this trigger is paused or not." + "description": "Indicate whether this schedule is paused or not." } } }, @@ -3257,7 +3257,7 @@ "description": "An optional periodic schedule for this job. The default behavior is that the job only runs when triggered by clicking “Run Now” in the Jobs UI or sending an API request to `runNow`.", "properties": { "pause_status": { - "description": "Whether this trigger is paused or not." + "description": "Indicate whether this schedule is paused or not." }, "quartz_cron_expression": { "description": "A Cron expression using Quartz syntax that describes the schedule for a job.\nSee [Cron Trigger](http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html)\nfor details. This field is required.\"\n" @@ -3317,7 +3317,7 @@ "description": "Optional schema to write to. This parameter is only used when a warehouse_id is also provided. If not provided, the `default` schema is used." }, "source": { - "description": "Optional location type of the notebook. When set to `WORKSPACE`, the notebook will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the notebook will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Notebook is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Notebook is located in cloud Git provider.\n" + "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" }, "warehouse_id": { "description": "ID of the SQL warehouse to connect to. If provided, we automatically generate and provide the profile and connection details to dbt. It can be overridden on a per-command basis by using the `--profiles-dir` command line argument." @@ -3981,7 +3981,7 @@ "description": "Path of the SQL file. Must be relative if the source is a remote Git repository and absolute for workspace paths." }, "source": { - "description": "Optional location type of the notebook. When set to `WORKSPACE`, the notebook will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the notebook will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Notebook is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Notebook is located in cloud Git provider.\n" + "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" } } }, @@ -4083,7 +4083,7 @@ } }, "pause_status": { - "description": "Whether this trigger is paused or not." + "description": "Indicate whether this schedule is paused or not." }, "table": { "description": "Table trigger settings.", @@ -5063,7 +5063,53 @@ "variables": { "description": "", "additionalproperties": { - "description": "" + "description": "", + "properties": { + "default": { + "description": "" + }, + "description": { + "description": "" + }, + "lookup": { + "description": "", + "properties": { + "alert": { + "description": "" + }, + "cluster": { + "description": "" + }, + "cluster_policy": { + "description": "" + }, + "dashboard": { + "description": "" + }, + "instance_pool": { + "description": "" + }, + "job": { + "description": "" + }, + "metastore": { + "description": "" + }, + "pipeline": { + "description": "" + }, + "query": { + "description": "" + }, + "service_principal": { + "description": "" + }, + "warehouse": { + "description": "" + } + } + } + } } }, "workspace": { @@ -5222,4 +5268,4 @@ } } } -} +} \ No newline at end of file diff --git a/cmd/account/budgets/budgets.go b/cmd/account/budgets/budgets.go index f0544abc5c4..dfa2f6bc4cb 100755 --- a/cmd/account/budgets/budgets.go +++ b/cmd/account/budgets/budgets.go @@ -282,7 +282,7 @@ func newList() *cobra.Command { ctx := cmd.Context() a := root.AccountClient(ctx) response := a.Budgets.List(ctx) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/account/custom-app-integration/custom-app-integration.go b/cmd/account/custom-app-integration/custom-app-integration.go index 3152e28fd56..79c0f8373ff 100755 --- a/cmd/account/custom-app-integration/custom-app-integration.go +++ b/cmd/account/custom-app-integration/custom-app-integration.go @@ -263,7 +263,7 @@ func newList() *cobra.Command { ctx := cmd.Context() a := root.AccountClient(ctx) response := a.CustomAppIntegration.List(ctx) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/account/groups/groups.go b/cmd/account/groups/groups.go index 7c344a99aa7..a068fba45c4 100755 --- a/cmd/account/groups/groups.go +++ b/cmd/account/groups/groups.go @@ -315,7 +315,7 @@ func newList() *cobra.Command { a := root.AccountClient(ctx) response := a.Groups.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/account/ip-access-lists/ip-access-lists.go b/cmd/account/ip-access-lists/ip-access-lists.go index 85d8369d4e0..dd836c90aaa 100755 --- a/cmd/account/ip-access-lists/ip-access-lists.go +++ b/cmd/account/ip-access-lists/ip-access-lists.go @@ -340,7 +340,7 @@ func newList() *cobra.Command { ctx := cmd.Context() a := root.AccountClient(ctx) response := a.IpAccessLists.List(ctx) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/account/log-delivery/log-delivery.go b/cmd/account/log-delivery/log-delivery.go index fc60864333b..eed8942b8f2 100755 --- a/cmd/account/log-delivery/log-delivery.go +++ b/cmd/account/log-delivery/log-delivery.go @@ -304,7 +304,7 @@ func newList() *cobra.Command { a := root.AccountClient(ctx) response := a.LogDelivery.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/account/metastore-assignments/metastore-assignments.go b/cmd/account/metastore-assignments/metastore-assignments.go index ec4a747f588..b1d0508b343 100755 --- a/cmd/account/metastore-assignments/metastore-assignments.go +++ b/cmd/account/metastore-assignments/metastore-assignments.go @@ -295,7 +295,7 @@ func newList() *cobra.Command { listReq.MetastoreId = args[0] response := a.MetastoreAssignments.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/account/metastores/metastores.go b/cmd/account/metastores/metastores.go index fa95e5f2c56..e8b7c8f7027 100755 --- a/cmd/account/metastores/metastores.go +++ b/cmd/account/metastores/metastores.go @@ -258,7 +258,7 @@ func newList() *cobra.Command { ctx := cmd.Context() a := root.AccountClient(ctx) response := a.Metastores.List(ctx) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/account/network-connectivity/network-connectivity.go b/cmd/account/network-connectivity/network-connectivity.go index dad41fe256b..bfe116f28e9 100755 --- a/cmd/account/network-connectivity/network-connectivity.go +++ b/cmd/account/network-connectivity/network-connectivity.go @@ -547,7 +547,7 @@ func newListNetworkConnectivityConfigurations() *cobra.Command { a := root.AccountClient(ctx) response := a.NetworkConnectivity.ListNetworkConnectivityConfigurations(ctx, listNetworkConnectivityConfigurationsReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. @@ -610,7 +610,7 @@ func newListPrivateEndpointRules() *cobra.Command { listPrivateEndpointRulesReq.NetworkConnectivityConfigId = args[0] response := a.NetworkConnectivity.ListPrivateEndpointRules(ctx, listPrivateEndpointRulesReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/account/o-auth-published-apps/o-auth-published-apps.go b/cmd/account/o-auth-published-apps/o-auth-published-apps.go index 5a0b7e31cad..1ce363ac929 100755 --- a/cmd/account/o-auth-published-apps/o-auth-published-apps.go +++ b/cmd/account/o-auth-published-apps/o-auth-published-apps.go @@ -73,7 +73,7 @@ func newList() *cobra.Command { a := root.AccountClient(ctx) response := a.OAuthPublishedApps.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/account/published-app-integration/published-app-integration.go b/cmd/account/published-app-integration/published-app-integration.go index b38395c1048..54cf6337140 100755 --- a/cmd/account/published-app-integration/published-app-integration.go +++ b/cmd/account/published-app-integration/published-app-integration.go @@ -263,7 +263,7 @@ func newList() *cobra.Command { ctx := cmd.Context() a := root.AccountClient(ctx) response := a.PublishedAppIntegration.List(ctx) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/account/service-principal-secrets/service-principal-secrets.go b/cmd/account/service-principal-secrets/service-principal-secrets.go index eb7ef60651c..1a646e25c3b 100755 --- a/cmd/account/service-principal-secrets/service-principal-secrets.go +++ b/cmd/account/service-principal-secrets/service-principal-secrets.go @@ -227,7 +227,7 @@ func newList() *cobra.Command { } response := a.ServicePrincipalSecrets.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/account/service-principals/service-principals.go b/cmd/account/service-principals/service-principals.go index 709f2647c64..af18d53415c 100755 --- a/cmd/account/service-principals/service-principals.go +++ b/cmd/account/service-principals/service-principals.go @@ -314,7 +314,7 @@ func newList() *cobra.Command { a := root.AccountClient(ctx) response := a.ServicePrincipals.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/account/users/users.go b/cmd/account/users/users.go index e2d3555cc09..f5b81f219d3 100755 --- a/cmd/account/users/users.go +++ b/cmd/account/users/users.go @@ -330,7 +330,7 @@ func newList() *cobra.Command { a := root.AccountClient(ctx) response := a.Users.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/account/workspace-assignment/workspace-assignment.go b/cmd/account/workspace-assignment/workspace-assignment.go index 396c5290dda..ab82cd39fad 100755 --- a/cmd/account/workspace-assignment/workspace-assignment.go +++ b/cmd/account/workspace-assignment/workspace-assignment.go @@ -220,7 +220,7 @@ func newList() *cobra.Command { } response := a.WorkspaceAssignment.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/catalogs/catalogs.go b/cmd/workspace/catalogs/catalogs.go index f44bdc18d93..8e639023f5e 100755 --- a/cmd/workspace/catalogs/catalogs.go +++ b/cmd/workspace/catalogs/catalogs.go @@ -293,7 +293,7 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.Catalogs.List(ctx) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/clean-rooms/clean-rooms.go b/cmd/workspace/clean-rooms/clean-rooms.go index dcdbf163002..4cee2ce6cf0 100755 --- a/cmd/workspace/clean-rooms/clean-rooms.go +++ b/cmd/workspace/clean-rooms/clean-rooms.go @@ -283,7 +283,7 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.CleanRooms.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/cluster-policies/cluster-policies.go b/cmd/workspace/cluster-policies/cluster-policies.go index bfe50523655..f6edee2b3d6 100755 --- a/cmd/workspace/cluster-policies/cluster-policies.go +++ b/cmd/workspace/cluster-policies/cluster-policies.go @@ -604,7 +604,7 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.ClusterPolicies.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/clusters/clusters.go b/cmd/workspace/clusters/clusters.go index 9f49beb5990..cf35b283751 100755 --- a/cmd/workspace/clusters/clusters.go +++ b/cmd/workspace/clusters/clusters.go @@ -654,7 +654,7 @@ func newEvents() *cobra.Command { } response := w.Clusters.Events(ctx, eventsReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. @@ -955,7 +955,7 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Clusters.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/connections/connections.go b/cmd/workspace/connections/connections.go index 71f4ba94b42..f740c7789fd 100755 --- a/cmd/workspace/connections/connections.go +++ b/cmd/workspace/connections/connections.go @@ -294,7 +294,7 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.Connections.List(ctx) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/dashboards/dashboards.go b/cmd/workspace/dashboards/dashboards.go index 6da048021bf..e07f739261b 100755 --- a/cmd/workspace/dashboards/dashboards.go +++ b/cmd/workspace/dashboards/dashboards.go @@ -294,7 +294,7 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Dashboards.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/experiments/experiments.go b/cmd/workspace/experiments/experiments.go index 653e8fe242e..368ec7f9460 100755 --- a/cmd/workspace/experiments/experiments.go +++ b/cmd/workspace/experiments/experiments.go @@ -734,7 +734,7 @@ func newGetHistory() *cobra.Command { getHistoryReq.MetricKey = args[0] response := w.Experiments.GetHistory(ctx, getHistoryReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. @@ -996,7 +996,7 @@ func newListArtifacts() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Experiments.ListArtifacts(ctx, listArtifactsReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. @@ -1056,7 +1056,7 @@ func newListExperiments() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Experiments.ListExperiments(ctx, listExperimentsReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. @@ -1834,7 +1834,7 @@ func newSearchExperiments() *cobra.Command { } response := w.Experiments.SearchExperiments(ctx, searchExperimentsReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. @@ -1908,7 +1908,7 @@ func newSearchRuns() *cobra.Command { } response := w.Experiments.SearchRuns(ctx, searchRunsReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/external-locations/external-locations.go b/cmd/workspace/external-locations/external-locations.go index 5d0b9e1c3f4..7ddc0d84257 100755 --- a/cmd/workspace/external-locations/external-locations.go +++ b/cmd/workspace/external-locations/external-locations.go @@ -320,7 +320,7 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.ExternalLocations.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/functions/functions.go b/cmd/workspace/functions/functions.go index d1f7935854f..d1db1ec9703 100755 --- a/cmd/workspace/functions/functions.go +++ b/cmd/workspace/functions/functions.go @@ -328,7 +328,7 @@ func newList() *cobra.Command { listReq.SchemaName = args[1] response := w.Functions.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/git-credentials/git-credentials.go b/cmd/workspace/git-credentials/git-credentials.go index 6e563e85a86..8984a953877 100755 --- a/cmd/workspace/git-credentials/git-credentials.go +++ b/cmd/workspace/git-credentials/git-credentials.go @@ -312,7 +312,7 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.GitCredentials.List(ctx) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/global-init-scripts/global-init-scripts.go b/cmd/workspace/global-init-scripts/global-init-scripts.go index 389ef86d0a3..de08614fed3 100755 --- a/cmd/workspace/global-init-scripts/global-init-scripts.go +++ b/cmd/workspace/global-init-scripts/global-init-scripts.go @@ -310,7 +310,7 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.GlobalInitScripts.List(ctx) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/groups/groups.go b/cmd/workspace/groups/groups.go index a6a9fdf7e89..aba54b8be6b 100755 --- a/cmd/workspace/groups/groups.go +++ b/cmd/workspace/groups/groups.go @@ -315,7 +315,7 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Groups.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/instance-pools/instance-pools.go b/cmd/workspace/instance-pools/instance-pools.go index 8529b027d6c..c9389fef86e 100755 --- a/cmd/workspace/instance-pools/instance-pools.go +++ b/cmd/workspace/instance-pools/instance-pools.go @@ -603,7 +603,7 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.InstancePools.List(ctx) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/instance-profiles/instance-profiles.go b/cmd/workspace/instance-profiles/instance-profiles.go index a6935348e23..2077c4bfc55 100755 --- a/cmd/workspace/instance-profiles/instance-profiles.go +++ b/cmd/workspace/instance-profiles/instance-profiles.go @@ -252,7 +252,7 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.InstanceProfiles.List(ctx) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/ip-access-lists/ip-access-lists.go b/cmd/workspace/ip-access-lists/ip-access-lists.go index 08a868a72b2..9eb08cb438d 100755 --- a/cmd/workspace/ip-access-lists/ip-access-lists.go +++ b/cmd/workspace/ip-access-lists/ip-access-lists.go @@ -341,7 +341,7 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.IpAccessLists.List(ctx) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/jobs/jobs.go b/cmd/workspace/jobs/jobs.go index 7f539a8f64b..957aa609341 100755 --- a/cmd/workspace/jobs/jobs.go +++ b/cmd/workspace/jobs/jobs.go @@ -1043,7 +1043,7 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Jobs.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. @@ -1110,7 +1110,7 @@ func newListRuns() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Jobs.ListRuns(ctx, listRunsReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/libraries/libraries.go b/cmd/workspace/libraries/libraries.go index e7174cadaba..fef81c25f6f 100755 --- a/cmd/workspace/libraries/libraries.go +++ b/cmd/workspace/libraries/libraries.go @@ -158,7 +158,7 @@ func newClusterStatus() *cobra.Command { clusterStatusReq.ClusterId = args[0] response := w.Libraries.ClusterStatus(ctx, clusterStatusReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/metastores/metastores.go b/cmd/workspace/metastores/metastores.go index cb6e0d91895..d63576d4eae 100755 --- a/cmd/workspace/metastores/metastores.go +++ b/cmd/workspace/metastores/metastores.go @@ -456,7 +456,7 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.Metastores.List(ctx) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/model-registry/model-registry.go b/cmd/workspace/model-registry/model-registry.go index 2c21ca14852..9c6034b56b0 100755 --- a/cmd/workspace/model-registry/model-registry.go +++ b/cmd/workspace/model-registry/model-registry.go @@ -1129,7 +1129,7 @@ func newGetLatestVersions() *cobra.Command { } response := w.ModelRegistry.GetLatestVersions(ctx, getLatestVersionsReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. @@ -1518,7 +1518,7 @@ func newListModels() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.ModelRegistry.ListModels(ctx, listModelsReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. @@ -1581,7 +1581,7 @@ func newListTransitionRequests() *cobra.Command { listTransitionRequestsReq.Version = args[1] response := w.ModelRegistry.ListTransitionRequests(ctx, listTransitionRequestsReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. @@ -1643,7 +1643,7 @@ func newListWebhooks() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.ModelRegistry.ListWebhooks(ctx, listWebhooksReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. @@ -1889,7 +1889,7 @@ func newSearchModelVersions() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.ModelRegistry.SearchModelVersions(ctx, searchModelVersionsReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. @@ -1950,7 +1950,7 @@ func newSearchModels() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.ModelRegistry.SearchModels(ctx, searchModelsReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/model-versions/model-versions.go b/cmd/workspace/model-versions/model-versions.go index 8b1787c683b..b4492cb369d 100755 --- a/cmd/workspace/model-versions/model-versions.go +++ b/cmd/workspace/model-versions/model-versions.go @@ -316,7 +316,7 @@ func newList() *cobra.Command { listReq.FullName = args[0] response := w.ModelVersions.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/pipelines/pipelines.go b/cmd/workspace/pipelines/pipelines.go index 2dec03c9f38..4c2db6aa38a 100755 --- a/cmd/workspace/pipelines/pipelines.go +++ b/cmd/workspace/pipelines/pipelines.go @@ -537,7 +537,7 @@ func newListPipelineEvents() *cobra.Command { listPipelineEventsReq.PipelineId = args[0] response := w.Pipelines.ListPipelineEvents(ctx, listPipelineEventsReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. @@ -598,7 +598,7 @@ func newListPipelines() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Pipelines.ListPipelines(ctx, listPipelinesReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/policy-families/policy-families.go b/cmd/workspace/policy-families/policy-families.go index d6dbc45a3e8..c81d2e92c94 100755 --- a/cmd/workspace/policy-families/policy-families.go +++ b/cmd/workspace/policy-families/policy-families.go @@ -139,7 +139,7 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.PolicyFamilies.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/providers/providers.go b/cmd/workspace/providers/providers.go index 9a5bf86a230..2552964883c 100755 --- a/cmd/workspace/providers/providers.go +++ b/cmd/workspace/providers/providers.go @@ -324,7 +324,7 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Providers.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. @@ -399,7 +399,7 @@ func newListShares() *cobra.Command { listSharesReq.Name = args[0] response := w.Providers.ListShares(ctx, listSharesReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/queries/queries.go b/cmd/workspace/queries/queries.go index ccb532ec756..ef2de44667f 100755 --- a/cmd/workspace/queries/queries.go +++ b/cmd/workspace/queries/queries.go @@ -304,7 +304,7 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Queries.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/query-history/query-history.go b/cmd/workspace/query-history/query-history.go index 88d4a99cfd4..847461058e4 100755 --- a/cmd/workspace/query-history/query-history.go +++ b/cmd/workspace/query-history/query-history.go @@ -74,7 +74,7 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.QueryHistory.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/recipients/recipients.go b/cmd/workspace/recipients/recipients.go index 112d1675091..d7d432b9ce8 100755 --- a/cmd/workspace/recipients/recipients.go +++ b/cmd/workspace/recipients/recipients.go @@ -343,7 +343,7 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Recipients.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/registered-models/registered-models.go b/cmd/workspace/registered-models/registered-models.go index 8badab43a2a..98aec3bb316 100755 --- a/cmd/workspace/registered-models/registered-models.go +++ b/cmd/workspace/registered-models/registered-models.go @@ -451,7 +451,7 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.RegisteredModels.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/repos/repos.go b/cmd/workspace/repos/repos.go index 022e51a0c91..0c38183aa24 100755 --- a/cmd/workspace/repos/repos.go +++ b/cmd/workspace/repos/repos.go @@ -486,7 +486,7 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Repos.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/schemas/schemas.go b/cmd/workspace/schemas/schemas.go index 0f5c868cb85..ebdab2ab502 100755 --- a/cmd/workspace/schemas/schemas.go +++ b/cmd/workspace/schemas/schemas.go @@ -334,7 +334,7 @@ func newList() *cobra.Command { listReq.CatalogName = args[0] response := w.Schemas.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/secrets/secrets.go b/cmd/workspace/secrets/secrets.go index c3ed525762e..ec6423d06a0 100755 --- a/cmd/workspace/secrets/secrets.go +++ b/cmd/workspace/secrets/secrets.go @@ -591,7 +591,7 @@ func newListAcls() *cobra.Command { listAclsReq.Scope = args[0] response := w.Secrets.ListAcls(ctx, listAclsReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. @@ -639,7 +639,7 @@ func newListScopes() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.Secrets.ListScopes(ctx) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. @@ -707,7 +707,7 @@ func newListSecrets() *cobra.Command { listSecretsReq.Scope = args[0] response := w.Secrets.ListSecrets(ctx, listSecretsReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/service-principals/service-principals.go b/cmd/workspace/service-principals/service-principals.go index 832c81d9ce2..353c0876118 100755 --- a/cmd/workspace/service-principals/service-principals.go +++ b/cmd/workspace/service-principals/service-principals.go @@ -314,7 +314,7 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.ServicePrincipals.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/serving-endpoints/serving-endpoints.go b/cmd/workspace/serving-endpoints/serving-endpoints.go index b29bb9519a8..9424c5e4e75 100755 --- a/cmd/workspace/serving-endpoints/serving-endpoints.go +++ b/cmd/workspace/serving-endpoints/serving-endpoints.go @@ -544,7 +544,7 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.ServingEndpoints.List(ctx) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/shares/shares.go b/cmd/workspace/shares/shares.go index 9ab37d42e9a..2c0479a0a5c 100755 --- a/cmd/workspace/shares/shares.go +++ b/cmd/workspace/shares/shares.go @@ -282,7 +282,7 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.Shares.List(ctx) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/storage-credentials/storage-credentials.go b/cmd/workspace/storage-credentials/storage-credentials.go index c86fb62529b..4a0d8f309cc 100755 --- a/cmd/workspace/storage-credentials/storage-credentials.go +++ b/cmd/workspace/storage-credentials/storage-credentials.go @@ -337,7 +337,7 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.StorageCredentials.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/system-schemas/system-schemas.go b/cmd/workspace/system-schemas/system-schemas.go index 1d10f335b06..9b2392a6e9c 100755 --- a/cmd/workspace/system-schemas/system-schemas.go +++ b/cmd/workspace/system-schemas/system-schemas.go @@ -217,7 +217,7 @@ func newList() *cobra.Command { listReq.MetastoreId = args[0] response := w.SystemSchemas.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/tables/tables.go b/cmd/workspace/tables/tables.go index a840fd23363..d4e76587d8a 100755 --- a/cmd/workspace/tables/tables.go +++ b/cmd/workspace/tables/tables.go @@ -343,7 +343,7 @@ func newList() *cobra.Command { listReq.SchemaName = args[1] response := w.Tables.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. @@ -431,7 +431,7 @@ func newListSummaries() *cobra.Command { listSummariesReq.CatalogName = args[0] response := w.Tables.ListSummaries(ctx, listSummariesReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/token-management/token-management.go b/cmd/workspace/token-management/token-management.go index c013181fe64..1c2e2c37c07 100755 --- a/cmd/workspace/token-management/token-management.go +++ b/cmd/workspace/token-management/token-management.go @@ -423,7 +423,7 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.TokenManagement.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/tokens/tokens.go b/cmd/workspace/tokens/tokens.go index cb2a92981e9..5550acfa5d1 100755 --- a/cmd/workspace/tokens/tokens.go +++ b/cmd/workspace/tokens/tokens.go @@ -233,7 +233,7 @@ func newList() *cobra.Command { ctx := cmd.Context() w := root.WorkspaceClient(ctx) response := w.Tokens.List(ctx) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/users/users.go b/cmd/workspace/users/users.go index b1e54fb1186..078a712e4c5 100755 --- a/cmd/workspace/users/users.go +++ b/cmd/workspace/users/users.go @@ -427,7 +427,7 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Users.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/vector-search-endpoints/vector-search-endpoints.go b/cmd/workspace/vector-search-endpoints/vector-search-endpoints.go index 2d6324340c5..d6863b6606c 100755 --- a/cmd/workspace/vector-search-endpoints/vector-search-endpoints.go +++ b/cmd/workspace/vector-search-endpoints/vector-search-endpoints.go @@ -309,7 +309,7 @@ func newListEndpoints() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.VectorSearchEndpoints.ListEndpoints(ctx, listEndpointsReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/vector-search-indexes/vector-search-indexes.go b/cmd/workspace/vector-search-indexes/vector-search-indexes.go index de81949110e..6beca7d2199 100755 --- a/cmd/workspace/vector-search-indexes/vector-search-indexes.go +++ b/cmd/workspace/vector-search-indexes/vector-search-indexes.go @@ -390,7 +390,7 @@ func newListIndexes() *cobra.Command { listIndexesReq.EndpointName = args[0] response := w.VectorSearchIndexes.ListIndexes(ctx, listIndexesReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/volumes/volumes.go b/cmd/workspace/volumes/volumes.go index 0509af94a6e..12cafeaf861 100755 --- a/cmd/workspace/volumes/volumes.go +++ b/cmd/workspace/volumes/volumes.go @@ -293,7 +293,7 @@ func newList() *cobra.Command { listReq.SchemaName = args[1] response := w.Volumes.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/warehouses/warehouses.go b/cmd/workspace/warehouses/warehouses.go index ecf5d634925..2e9282a8533 100755 --- a/cmd/workspace/warehouses/warehouses.go +++ b/cmd/workspace/warehouses/warehouses.go @@ -662,7 +662,7 @@ func newList() *cobra.Command { w := root.WorkspaceClient(ctx) response := w.Warehouses.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/cmd/workspace/workspace/workspace.go b/cmd/workspace/workspace/workspace.go index 6168fb35c0d..4fb63f0c023 100755 --- a/cmd/workspace/workspace/workspace.go +++ b/cmd/workspace/workspace/workspace.go @@ -578,7 +578,7 @@ func newList() *cobra.Command { listReq.Path = args[0] response := w.Workspace.List(ctx, listReq) - return cmdio.Render(ctx, response) + return cmdio.RenderIterator(ctx, response) } // Disable completions since they are not applicable. diff --git a/libs/cmdio/reflect_iterator.go b/libs/cmdio/reflect_iterator.go deleted file mode 100644 index 56d84a575dd..00000000000 --- a/libs/cmdio/reflect_iterator.go +++ /dev/null @@ -1,45 +0,0 @@ -package cmdio - -import ( - "context" - "reflect" -) - -// Reflectively call Next and HasNext on listing.Iterator[*] values. -// -// Because listing.Iterator[T] has a type parameter, it isn't possible to -// use a normal switch statement to inspect whether a value implements this -// interface for some T. Instead, we resort to checking whether the provided -// object has HasNext() and Next() methods. -type reflectIterator struct { - hasNext reflect.Value - next reflect.Value -} - -func newReflectIterator(v any) (reflectIterator, bool) { - rv := reflect.ValueOf(v) - rt := rv.Type() - _, hasHasNext := rt.MethodByName("HasNext") - _, hasNext := rt.MethodByName("Next") - if hasNext && hasHasNext { - return reflectIterator{ - hasNext: rv.MethodByName("HasNext"), - next: rv.MethodByName("Next"), - }, true - } - return reflectIterator{}, false -} - -func (r reflectIterator) HasNext(ctx context.Context) bool { - res := r.hasNext.Call([]reflect.Value{reflect.ValueOf(ctx)}) - return res[0].Bool() -} - -func (r reflectIterator) Next(ctx context.Context) (any, error) { - res := r.next.Call([]reflect.Value{reflect.ValueOf(ctx)}) - item := res[0].Interface() - if res[1].IsNil() { - return item, nil - } - return item, res[1].Interface().(error) -} diff --git a/libs/cmdio/reflect_iterator_test.go b/libs/cmdio/reflect_iterator_test.go deleted file mode 100644 index 2c82e74de83..00000000000 --- a/libs/cmdio/reflect_iterator_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package cmdio - -import ( - "context" - "errors" - "testing" - - "github.com/databricks/databricks-sdk-go/listing" - "github.com/stretchr/testify/assert" -) - -type dummyIterator struct { - items []any -} - -func (d *dummyIterator) HasNext(_ context.Context) bool { - return len(d.items) > 0 -} - -func (d *dummyIterator) Next(ctx context.Context) (any, error) { - if !d.HasNext(ctx) { - return nil, errors.New("no more items") - } - item := d.items[0] - d.items = d.items[1:] - return item, nil -} - -var _ listing.Iterator[any] = &dummyIterator{} - -func TestReflectIterator_NonIterator(t *testing.T) { - _, ok := newReflectIterator(3) - assert.False(t, ok) -} - -func TestReflectIterator_DummyIterator(t *testing.T) { - ri, ok := newReflectIterator(&dummyIterator{items: []any{1, "2", true}}) - assert.True(t, ok) - ctx := context.Background() - first, err := ri.Next(ctx) - assert.NoError(t, err) - assert.Equal(t, first, 1) - second, err := ri.Next(ctx) - assert.NoError(t, err) - assert.Equal(t, second, "2") - third, err := ri.Next(ctx) - assert.NoError(t, err) - assert.Equal(t, third, true) - assert.False(t, ri.HasNext(ctx)) -} diff --git a/libs/cmdio/render.go b/libs/cmdio/render.go index 3438a833bfd..40cdde354d6 100644 --- a/libs/cmdio/render.go +++ b/libs/cmdio/render.go @@ -14,6 +14,7 @@ import ( "time" "github.com/databricks/cli/libs/flags" + "github.com/databricks/databricks-sdk-go/listing" "github.com/fatih/color" "github.com/nwidger/jsoncolor" ) @@ -81,19 +82,19 @@ func (r readerRenderer) renderText(_ context.Context, w io.Writer) error { return err } -type iteratorRenderer struct { - t reflectIterator +type iteratorRenderer[T any] struct { + t listing.Iterator[T] bufferSize int } -func (ir iteratorRenderer) getBufferSize() int { +func (ir iteratorRenderer[T]) getBufferSize() int { if ir.bufferSize == 0 { return 20 } return ir.bufferSize } -func (ir iteratorRenderer) renderJson(ctx context.Context, w writeFlusher) error { +func (ir iteratorRenderer[T]) renderJson(ctx context.Context, w writeFlusher) error { // Iterators are always rendered as a list of resources in JSON. _, err := w.Write([]byte("[\n ")) if err != nil { @@ -132,7 +133,7 @@ func (ir iteratorRenderer) renderJson(ctx context.Context, w writeFlusher) error return w.Flush() } -func (ir iteratorRenderer) renderTemplate(ctx context.Context, t *template.Template, w *tabwriter.Writer) error { +func (ir iteratorRenderer[T]) renderTemplate(ctx context.Context, t *template.Template, w *tabwriter.Writer) error { buf := make([]any, 0, ir.getBufferSize()) for i := 0; ir.t.HasNext(ctx); i++ { n, err := ir.t.Next(ctx) @@ -193,12 +194,13 @@ func newRenderer(t any) any { if r, ok := t.(io.Reader); ok { return readerRenderer{reader: r} } - if iterator, ok := newReflectIterator(t); ok { - return iteratorRenderer{t: iterator} - } return defaultRenderer{t: t} } +func newIteratorRenderer[T any](i listing.Iterator[T]) iteratorRenderer[T] { + return iteratorRenderer[T]{t: i} +} + type bufferedFlusher struct { w io.Writer b *bytes.Buffer @@ -248,21 +250,49 @@ func renderWithTemplate(r any, ctx context.Context, outputFormat flags.Output, w } } +type listingInterface interface { + HasNext(context.Context) bool +} + func Render(ctx context.Context, v any) error { c := fromContext(ctx) + if _, ok := v.(listingInterface); ok { + panic("use RenderIterator instead") + } return renderWithTemplate(newRenderer(v), ctx, c.outputFormat, c.out, c.headerTemplate, c.template) } +func RenderIterator[T any](ctx context.Context, i listing.Iterator[T]) error { + c := fromContext(ctx) + return renderWithTemplate(newIteratorRenderer(i), ctx, c.outputFormat, c.out, c.headerTemplate, c.template) +} + func RenderWithTemplate(ctx context.Context, v any, headerTemplate, template string) error { c := fromContext(ctx) + if _, ok := v.(listingInterface); ok { + panic("use RenderIteratorWithTemplate instead") + } return renderWithTemplate(newRenderer(v), ctx, c.outputFormat, c.out, headerTemplate, template) } +func RenderIteratorWithTemplate[T any](ctx context.Context, i listing.Iterator[T], headerTemplate, template string) error { + c := fromContext(ctx) + return renderWithTemplate(newIteratorRenderer(i), ctx, c.outputFormat, c.out, headerTemplate, template) +} + func RenderJson(ctx context.Context, v any) error { c := fromContext(ctx) + if _, ok := v.(listingInterface); ok { + panic("use RenderIteratorJson instead") + } return renderWithTemplate(newRenderer(v), ctx, flags.OutputJSON, c.out, c.headerTemplate, c.template) } +func RenderIteratorJson[T any](ctx context.Context, i listing.Iterator[T]) error { + c := fromContext(ctx) + return renderWithTemplate(newIteratorRenderer(i), ctx, c.outputFormat, c.out, c.headerTemplate, c.template) +} + func renderUsingTemplate(ctx context.Context, r templateRenderer, w io.Writer, headerTmpl, tmpl string) error { tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0) base := template.New("command").Funcs(template.FuncMap{ diff --git a/libs/cmdio/render_test.go b/libs/cmdio/render_test.go index aeca075426e..6bde446c4ea 100644 --- a/libs/cmdio/render_test.go +++ b/libs/cmdio/render_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" "strings" "testing" @@ -34,9 +35,26 @@ var dummyWorkspace2 = provisioning.Workspace{ WorkspaceName: "def", } -func makeWorkspaces(count int) []provisioning.Workspace { - res := make([]provisioning.Workspace, 0, count) - next := []provisioning.Workspace{dummyWorkspace1, dummyWorkspace2} +type dummyIterator struct { + items []*provisioning.Workspace +} + +func (d *dummyIterator) HasNext(_ context.Context) bool { + return len(d.items) > 0 +} + +func (d *dummyIterator) Next(ctx context.Context) (*provisioning.Workspace, error) { + if !d.HasNext(ctx) { + return nil, errors.New("no more items") + } + item := d.items[0] + d.items = d.items[1:] + return item, nil +} + +func makeWorkspaces(count int) []*provisioning.Workspace { + res := make([]*provisioning.Workspace, 0, count) + next := []*provisioning.Workspace{&dummyWorkspace1, &dummyWorkspace2} for i := 0; i < count; i++ { n := next[0] next = append(next[1:], n) @@ -45,11 +63,9 @@ func makeWorkspaces(count int) []provisioning.Workspace { return res } -func makeIterator(count int) listing.Iterator[any] { - items := make([]any, 0, count) - for _, ws := range makeWorkspaces(count) { - items = append(items, any(ws)) - } +func makeIterator(count int) listing.Iterator[*provisioning.Workspace] { + items := make([]*provisioning.Workspace, 0, count) + items = append(items, makeWorkspaces(count)...) return &dummyIterator{ items: items, } @@ -157,7 +173,12 @@ func TestRender(t *testing.T) { output := &bytes.Buffer{} cmdIO := NewIO(c.outputFormat, nil, output, output, c.headerTemplate, c.template) ctx := InContext(context.Background(), cmdIO) - err := Render(ctx, c.v) + var err error + if vv, ok := c.v.(listing.Iterator[*provisioning.Workspace]); ok { + err = RenderIterator(ctx, vv) + } else { + err = Render(ctx, c.v) + } if c.errMessage != "" { assert.ErrorContains(t, err, c.errMessage) } else { From 147031ef5f75384a8f9f57c30e73aa2507a25258 Mon Sep 17 00:00:00 2001 From: Andrew Nester Date: Wed, 21 Feb 2024 12:36:05 +0100 Subject: [PATCH 13/13] fix conflicts --- bundle/schema/docs/bundle_descriptions.json | 112 +++++++------------- 1 file changed, 40 insertions(+), 72 deletions(-) diff --git a/bundle/schema/docs/bundle_descriptions.json b/bundle/schema/docs/bundle_descriptions.json index 3b420fb7173..982dd4eb7f9 100644 --- a/bundle/schema/docs/bundle_descriptions.json +++ b/bundle/schema/docs/bundle_descriptions.json @@ -1,8 +1,8 @@ { - "description": "Root of the bundle config", + "description": "", "properties": { "artifacts": { - "description": "A description of all code artifacts in this bundle.", + "description": "", "additionalproperties": { "description": "", "properties": { @@ -33,7 +33,7 @@ } }, "bundle": { - "description": "The details for this bundle.", + "description": "", "properties": { "compute_id": { "description": "" @@ -58,7 +58,7 @@ } }, "name": { - "description": "The name of the bundle." + "description": "" } } }, @@ -77,7 +77,7 @@ } }, "include": { - "description": "A list of glob patterns of files to load and merge into the this configuration. Defaults to no files being included.", + "description": "", "items": { "description": "" } @@ -322,7 +322,7 @@ "description": "A unique name for the job cluster. This field is required and must be unique within the job.\n`JobTaskSettings` may refer to this field to determine which cluster to launch for the task execution." }, "new_cluster": { - "description": "If new_cluster, a description of a cluster that is created for each task.", + "description": "If new_cluster, a description of a cluster that is created for only for this task.", "properties": { "apply_policy_default_values": { "description": "" @@ -785,11 +785,7 @@ "description": "Optional schema to write to. This parameter is only used when a warehouse_id is also provided. If not provided, the `default` schema is used." }, "source": { -<<<<<<< HEAD - "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" -======= - "description": "Optional location type of the SQL file. When set to `WORKSPACE`, the SQL file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the SQL file will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: SQL file is located in \u003cDatabricks\u003e workspace.\n* `GIT`: SQL file is located in cloud Git provider.\n" ->>>>>>> main + "description": "Optional location type of the project directory. When set to `WORKSPACE`, the project will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the project will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Project is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Project is located in cloud Git provider.\n" }, "warehouse_id": { "description": "ID of the SQL warehouse to connect to. If provided, we automatically generate and provide the profile and connection details to dbt. It can be overridden on a per-command basis by using the `--profiles-dir` command line argument." @@ -934,7 +930,7 @@ "description": "An optional minimal interval in milliseconds between the start of the failed run and the subsequent retry run. The default behavior is that unsuccessful runs are immediately retried." }, "new_cluster": { - "description": "If new_cluster, a description of a cluster that is created for each task.", + "description": "If new_cluster, a description of a cluster that is created for only for this task.", "properties": { "apply_policy_default_values": { "description": "" @@ -1273,11 +1269,7 @@ "description": "The path of the notebook to be run in the Databricks workspace or remote repository.\nFor notebooks stored in the Databricks workspace, the path must be absolute and begin with a slash.\nFor notebooks stored in a remote repository, the path must be relative. This field is required.\n" }, "source": { -<<<<<<< HEAD - "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" -======= - "description": "Optional location type of the SQL file. When set to `WORKSPACE`, the SQL file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the SQL file will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: SQL file is located in \u003cDatabricks\u003e workspace.\n* `GIT`: SQL file is located in cloud Git provider.\n" ->>>>>>> main + "description": "Optional location type of the project directory. When set to `WORKSPACE`, the project will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the project will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Project is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Project is located in cloud Git provider.\n" } } }, @@ -1379,11 +1371,7 @@ "description": "The Python file to be executed. Cloud file URIs (such as dbfs:/, s3:/, adls:/, gcs:/) and workspace paths are supported. For python files stored in the Databricks workspace, the path must be absolute and begin with `/`. For files stored in a remote repository, the path must be relative. This field is required." }, "source": { -<<<<<<< HEAD - "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" -======= - "description": "Optional location type of the SQL file. When set to `WORKSPACE`, the SQL file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the SQL file will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: SQL file is located in \u003cDatabricks\u003e workspace.\n* `GIT`: SQL file is located in cloud Git provider.\n" ->>>>>>> main + "description": "Optional location type of the project directory. When set to `WORKSPACE`, the project will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the project will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Project is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Project is located in cloud Git provider.\n" } } }, @@ -1461,11 +1449,7 @@ "description": "Path of the SQL file. Must be relative if the source is a remote Git repository and absolute for workspace paths." }, "source": { -<<<<<<< HEAD - "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" -======= - "description": "Optional location type of the SQL file. When set to `WORKSPACE`, the SQL file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the SQL file will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: SQL file is located in \u003cDatabricks\u003e workspace.\n* `GIT`: SQL file is located in cloud Git provider.\n" ->>>>>>> main + "description": "Optional location type of the project directory. When set to `WORKSPACE`, the project will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the project will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Project is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Project is located in cloud Git provider.\n" } } }, @@ -2551,7 +2535,7 @@ "description": "", "properties": { "artifacts": { - "description": "A description of all code artifacts in this bundle.", + "description": "", "additionalproperties": { "description": "", "properties": { @@ -2582,7 +2566,7 @@ } }, "bundle": { - "description": "The details for this bundle.", + "description": "", "properties": { "compute_id": { "description": "" @@ -2607,7 +2591,7 @@ } }, "name": { - "description": "The name of the bundle." + "description": "" } } }, @@ -2871,7 +2855,7 @@ "description": "A unique name for the job cluster. This field is required and must be unique within the job.\n`JobTaskSettings` may refer to this field to determine which cluster to launch for the task execution." }, "new_cluster": { - "description": "If new_cluster, a description of a cluster that is created for each task.", + "description": "If new_cluster, a description of a cluster that is created for only for this task.", "properties": { "apply_policy_default_values": { "description": "" @@ -3334,11 +3318,7 @@ "description": "Optional schema to write to. This parameter is only used when a warehouse_id is also provided. If not provided, the `default` schema is used." }, "source": { -<<<<<<< HEAD - "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" -======= - "description": "Optional location type of the SQL file. When set to `WORKSPACE`, the SQL file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the SQL file will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: SQL file is located in \u003cDatabricks\u003e workspace.\n* `GIT`: SQL file is located in cloud Git provider.\n" ->>>>>>> main + "description": "Optional location type of the project directory. When set to `WORKSPACE`, the project will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the project will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Project is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Project is located in cloud Git provider.\n" }, "warehouse_id": { "description": "ID of the SQL warehouse to connect to. If provided, we automatically generate and provide the profile and connection details to dbt. It can be overridden on a per-command basis by using the `--profiles-dir` command line argument." @@ -3483,7 +3463,7 @@ "description": "An optional minimal interval in milliseconds between the start of the failed run and the subsequent retry run. The default behavior is that unsuccessful runs are immediately retried." }, "new_cluster": { - "description": "If new_cluster, a description of a cluster that is created for each task.", + "description": "If new_cluster, a description of a cluster that is created for only for this task.", "properties": { "apply_policy_default_values": { "description": "" @@ -3822,11 +3802,7 @@ "description": "The path of the notebook to be run in the Databricks workspace or remote repository.\nFor notebooks stored in the Databricks workspace, the path must be absolute and begin with a slash.\nFor notebooks stored in a remote repository, the path must be relative. This field is required.\n" }, "source": { -<<<<<<< HEAD - "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" -======= - "description": "Optional location type of the SQL file. When set to `WORKSPACE`, the SQL file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the SQL file will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: SQL file is located in \u003cDatabricks\u003e workspace.\n* `GIT`: SQL file is located in cloud Git provider.\n" ->>>>>>> main + "description": "Optional location type of the project directory. When set to `WORKSPACE`, the project will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the project will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Project is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Project is located in cloud Git provider.\n" } } }, @@ -3928,11 +3904,7 @@ "description": "The Python file to be executed. Cloud file URIs (such as dbfs:/, s3:/, adls:/, gcs:/) and workspace paths are supported. For python files stored in the Databricks workspace, the path must be absolute and begin with `/`. For files stored in a remote repository, the path must be relative. This field is required." }, "source": { -<<<<<<< HEAD - "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" -======= - "description": "Optional location type of the SQL file. When set to `WORKSPACE`, the SQL file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the SQL file will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: SQL file is located in \u003cDatabricks\u003e workspace.\n* `GIT`: SQL file is located in cloud Git provider.\n" ->>>>>>> main + "description": "Optional location type of the project directory. When set to `WORKSPACE`, the project will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the project will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Project is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Project is located in cloud Git provider.\n" } } }, @@ -4010,11 +3982,7 @@ "description": "Path of the SQL file. Must be relative if the source is a remote Git repository and absolute for workspace paths." }, "source": { -<<<<<<< HEAD - "description": "Optional location type of the Python file. When set to `WORKSPACE` or not specified, the file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace or cloud location (if the `python_file` has a URI format). When set to `GIT`,\nthe Python file will be retrieved from a Git repository defined in `git_source`.\n\n* `WORKSPACE`: The Python file is located in a \u003cDatabricks\u003e workspace or at a cloud filesystem URI.\n* `GIT`: The Python file is located in a remote Git repository.\n" -======= - "description": "Optional location type of the SQL file. When set to `WORKSPACE`, the SQL file will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the SQL file will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: SQL file is located in \u003cDatabricks\u003e workspace.\n* `GIT`: SQL file is located in cloud Git provider.\n" ->>>>>>> main + "description": "Optional location type of the project directory. When set to `WORKSPACE`, the project will be retrieved\nfrom the local \u003cDatabricks\u003e workspace. When set to `GIT`, the project will be retrieved from a Git repository\ndefined in `git_source`. If the value is empty, the task will use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.\n\n* `WORKSPACE`: Project is located in \u003cDatabricks\u003e workspace.\n* `GIT`: Project is located in cloud Git provider.\n" } } }, @@ -5147,10 +5115,10 @@ } }, "workspace": { - "description": "Configures which workspace to connect to and locations for files, state, and similar locations within the workspace file tree.", + "description": "", "properties": { "artifact_path": { - "description": "The remote path to synchronize build artifacts to. This defaults to `${workspace.root}/artifacts`" + "description": "" }, "auth_type": { "description": "" @@ -5159,10 +5127,10 @@ "description": "" }, "azure_environment": { - "description": "Azure environment, one of (Public, UsGov, China, Germany)." + "description": "" }, "azure_login_app_id": { - "description": "Azure Login Application ID." + "description": "" }, "azure_tenant_id": { "description": "" @@ -5171,28 +5139,28 @@ "description": "" }, "azure_workspace_resource_id": { - "description": "Azure Resource Manager ID for Azure Databricks workspace." + "description": "" }, "client_id": { "description": "" }, "file_path": { - "description": "The remote path to synchronize local files artifacts to. This defaults to `${workspace.root}/files`" + "description": "" }, "google_service_account": { "description": "" }, "host": { - "description": "Host url of the workspace." + "description": "" }, "profile": { - "description": "Connection profile to use. By default profiles are specified in ~/.databrickscfg." + "description": "" }, "root_path": { - "description": "The base location for synchronizing files, artifacts and state. Defaults to `/Users/jane@doe.com/.bundle/${bundle.name}/${bundle.target}`" + "description": "" }, "state_path": { - "description": "The remote path to synchronize bundle state to. This defaults to `${workspace.root}/state`" + "description": "" } } } @@ -5252,10 +5220,10 @@ } }, "workspace": { - "description": "Configures which workspace to connect to and locations for files, state, and similar locations within the workspace file tree.", + "description": "", "properties": { "artifact_path": { - "description": "The remote path to synchronize build artifacts to. This defaults to `${workspace.root}/artifacts`" + "description": "" }, "auth_type": { "description": "" @@ -5264,10 +5232,10 @@ "description": "" }, "azure_environment": { - "description": "Azure environment, one of (Public, UsGov, China, Germany)." + "description": "" }, "azure_login_app_id": { - "description": "Azure Login Application ID." + "description": "" }, "azure_tenant_id": { "description": "" @@ -5276,28 +5244,28 @@ "description": "" }, "azure_workspace_resource_id": { - "description": "Azure Resource Manager ID for Azure Databricks workspace." + "description": "" }, "client_id": { "description": "" }, "file_path": { - "description": "The remote path to synchronize local files artifacts to. This defaults to `${workspace.root}/files`" + "description": "" }, "google_service_account": { "description": "" }, "host": { - "description": "Host url of the workspace." + "description": "" }, "profile": { - "description": "Connection profile to use. By default profiles are specified in ~/.databrickscfg." + "description": "" }, "root_path": { - "description": "The base location for synchronizing files, artifacts and state. Defaults to `/Users/jane@doe.com/.bundle/${bundle.name}/${bundle.target}`" + "description": "" }, "state_path": { - "description": "The remote path to synchronize bundle state to. This defaults to `${workspace.root}/state`" + "description": "" } } }