From a1ce8652410b8846874277400fcdf42310a808af Mon Sep 17 00:00:00 2001 From: yuaanlin Date: Thu, 19 Feb 2026 01:34:00 +0800 Subject: [PATCH] fix: auto-resolve IDs and strip ObjectID prefixes for better UX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ObjectID() now auto-strips prefixes (e.g. "service-xxx" → "xxx") so users can pass prefixed IDs without errors - Log command resolves projectID and environmentID from serviceID instead of relying on context (which may point to a different project) - Refactored log API to use proper subscription parameters (projectID, serviceID, environmentID) matching backend GraphQL schema - Auto-resolve environmentID across all commands that need it (deploy, service operations) since environments are deprecated - Use graphql-ws protocol for WebSocket subscriptions Co-Authored-By: Claude Opus 4.6 --- internal/cmd/deployment/get/get.go | 9 ++ internal/cmd/deployment/list/list.go | 9 ++ internal/cmd/deployment/log/log.go | 179 +++++++++++++--------- internal/cmd/project/export/export.go | 11 +- internal/cmd/service/delete/delete.go | 9 ++ internal/cmd/service/expose/expose.go | 9 ++ internal/cmd/service/metric/metric.go | 9 ++ internal/cmd/service/redeploy/redeploy.go | 9 ++ internal/cmd/service/restart/restart.go | 9 ++ internal/cmd/service/suspend/suspend.go | 9 ++ internal/cmd/service/update/tag/tag.go | 9 ++ internal/util/env.go | 28 +++- pkg/api/client.go | 1 + pkg/api/common.go | 22 ++- pkg/api/interface.go | 9 +- pkg/api/log.go | 122 ++++++++++----- 16 files changed, 316 insertions(+), 137 deletions(-) diff --git a/internal/cmd/deployment/get/get.go b/internal/cmd/deployment/get/get.go index a2911c4..68f8609 100644 --- a/internal/cmd/deployment/get/get.go +++ b/internal/cmd/deployment/get/get.go @@ -69,6 +69,15 @@ func runGetInteractive(f *cmdutil.Factory, opts *Options) error { } func runGetNonInteractive(f *cmdutil.Factory, opts *Options) (err error) { + if opts.deploymentID == "" && opts.environmentID == "" { + projectID := f.Config.GetContext().GetProject().GetID() + envID, resolveErr := util.ResolveEnvironmentID(f.ApiClient, projectID) + if resolveErr != nil { + return resolveErr + } + opts.environmentID = envID + } + if err = paramCheck(opts); err != nil { return err } diff --git a/internal/cmd/deployment/list/list.go b/internal/cmd/deployment/list/list.go index 62249a6..3c34c69 100644 --- a/internal/cmd/deployment/list/list.go +++ b/internal/cmd/deployment/list/list.go @@ -65,6 +65,15 @@ func runListInteractive(f *cmdutil.Factory, opts *Options) error { } func runListNonInteractive(f *cmdutil.Factory, opts *Options) error { + if opts.environmentID == "" { + projectID := f.Config.GetContext().GetProject().GetID() + envID, err := util.ResolveEnvironmentID(f.ApiClient, projectID) + if err != nil { + return err + } + opts.environmentID = envID + } + if err := paramCheck(opts); err != nil { return err } diff --git a/internal/cmd/deployment/log/log.go b/internal/cmd/deployment/log/log.go index d057909..d241071 100644 --- a/internal/cmd/deployment/log/log.go +++ b/internal/cmd/deployment/log/log.go @@ -13,6 +13,7 @@ import ( ) type Options struct { + projectID string serviceID string serviceName string environmentID string @@ -42,6 +43,7 @@ func NewCmdLog(f *cmdutil.Factory) *cobra.Command { zctx := f.Config.GetContext() + cmd.Flags().StringVar(&opts.projectID, "project-id", zctx.GetProject().GetID(), "Project ID") cmd.Flags().StringVar(&opts.deploymentID, "deployment-id", "", "Deployment ID") cmd.Flags().StringVar(&opts.serviceID, "service-id", zctx.GetService().GetID(), "Service ID") cmd.Flags().StringVar(&opts.serviceName, "service-name", zctx.GetService().GetName(), "Service Name") @@ -61,8 +63,13 @@ func runLog(f *cmdutil.Factory, opts *Options) error { } func runLogInteractive(f *cmdutil.Factory, opts *Options) error { + zctx := f.Config.GetContext() + + if opts.projectID == "" { + opts.projectID = zctx.GetProject().GetID() + } + if opts.deploymentID == "" { - zctx := f.Config.GetContext() _, err := f.ParamFiller.ServiceByNameWithEnvironment(fill.ServiceByNameWithEnvironmentOptions{ ProjectCtx: zctx, ServiceID: &opts.serviceID, @@ -79,114 +86,134 @@ func runLogInteractive(f *cmdutil.Factory, opts *Options) error { } func runLogNonInteractive(f *cmdutil.Factory, opts *Options) (err error) { - if err = paramCheck(opts); err != nil { - return err + // Resolve serviceID from serviceName first + if opts.serviceID == "" && opts.serviceName != "" { + service, err := util.GetServiceByName(f.Config, f.ApiClient, opts.serviceName) + if err != nil { + return fmt.Errorf("failed to get service: %w", err) + } + opts.serviceID = service.ID } - if opts.watch { - if opts.deploymentID != "" { - var logChan <-chan model.Log - var subscriptionErr error - - switch opts.logType { - case logTypeRuntime: - logChan, subscriptionErr = f.ApiClient.WatchRuntimeLogs(context.Background(), opts.deploymentID) - case logTypeBuild: - logChan, subscriptionErr = f.ApiClient.WatchBuildLogs(context.Background(), opts.deploymentID) - default: - logChan, subscriptionErr = f.ApiClient.WatchRuntimeLogs(context.Background(), opts.deploymentID) - } - if subscriptionErr != nil { - return fmt.Errorf("failed to watch logs: %w", err) - } - - for log := range logChan { - f.Printer.Table(log.Header(), log.Rows()) - } + // When serviceID is available, always resolve projectID and environmentID from the service + // instead of relying on context (which may point to a different project). + if opts.serviceID != "" { + service, err := f.ApiClient.GetService(context.Background(), opts.serviceID, "", "", "") + if err != nil { + return fmt.Errorf("failed to get service: %w", err) } - - return nil - } else { - var logs model.Logs - - // If deployment id is provided, get deployment by deployment id - if opts.deploymentID != "" { - logs, err = logDeploymentByID(f, opts.deploymentID, opts.logType) - } else { - // or, get deployment by service id and environment id - - // If service id is not provided, get service id by service name - if opts.serviceID == "" { - var service *model.Service - if service, err = util.GetServiceByName(f.Config, f.ApiClient, opts.serviceName); err != nil { - return fmt.Errorf("failed to get service: %w", err) - } else { - opts.serviceID = service.ID - } - } - logs, err = logDeploymentByServiceAndEnvironment(f, opts.serviceID, opts.environmentID, opts.logType) + if service.Project != nil { + opts.projectID = service.Project.ID + } + envID, resolveErr := util.ResolveEnvironmentID(f.ApiClient, opts.projectID) + if resolveErr != nil { + return resolveErr } + opts.environmentID = envID + } - if err != nil { - return err + // Fallback: resolve environmentID from context project if still empty + if opts.deploymentID == "" && opts.environmentID == "" { + projectID := opts.projectID + if projectID == "" { + projectID = f.Config.GetContext().GetProject().GetID() } + envID, resolveErr := util.ResolveEnvironmentID(f.ApiClient, projectID) + if resolveErr != nil { + return resolveErr + } + opts.environmentID = envID + } - f.Printer.Table(logs.Header(), logs.Rows()) + if err = paramCheck(opts); err != nil { + return err + } - return nil + if opts.watch { + return watchLogs(f, opts) } + return queryLogs(f, opts) } -func logDeploymentByID(f *cmdutil.Factory, deploymentID, logType string) (model.Logs, error) { - switch logType { +func queryLogs(f *cmdutil.Factory, opts *Options) error { + var logs model.Logs + var err error + + switch opts.logType { case logTypeRuntime: - logs, err := f.ApiClient.GetRuntimeLogs(context.Background(), deploymentID, "", "") + logs, err = f.ApiClient.GetRuntimeLogs(context.Background(), opts.serviceID, opts.environmentID, opts.deploymentID) if err != nil { - return nil, fmt.Errorf("failed to get runtime logs: %w", err) + return fmt.Errorf("failed to get runtime logs: %w", err) } - return logs, nil case logTypeBuild: - logs, err := f.ApiClient.GetBuildLogs(context.Background(), deploymentID) + deploymentID := opts.deploymentID + if deploymentID == "" { + deployment, exist, e := f.ApiClient.GetLatestDeployment(context.Background(), opts.serviceID, opts.environmentID) + if e != nil { + return fmt.Errorf("failed to get latest deployment: %w", e) + } + if !exist { + return fmt.Errorf("no deployment found for service %s and environment %s", opts.serviceID, opts.environmentID) + } + deploymentID = deployment.ID + f.Log.Infof("Deployment ID: %s", deploymentID) + } + logs, err = f.ApiClient.GetBuildLogs(context.Background(), deploymentID) if err != nil { - return nil, fmt.Errorf("failed to get build logs: %w", err) + return fmt.Errorf("failed to get build logs: %w", err) } - return logs, nil default: - return nil, fmt.Errorf("unknown log type: %s", logType) + return fmt.Errorf("unknown log type: %s", opts.logType) } + + f.Printer.Table(logs.Header(), logs.Rows()) + return nil } -func logDeploymentByServiceAndEnvironment(f *cmdutil.Factory, serviceID, environmentID, logType string) (model.Logs, error) { - switch logType { +func watchLogs(f *cmdutil.Factory, opts *Options) error { + var logChan <-chan model.Log + var err error + + switch opts.logType { case logTypeRuntime: - logs, err := f.ApiClient.GetRuntimeLogs(context.Background(), "", serviceID, environmentID) - if err != nil { - return nil, fmt.Errorf("failed to get runtime logs: %w", err) + if opts.serviceID == "" || opts.environmentID == "" { + return errors.New("service-id and env-id are required for watching runtime logs") } - return logs, nil + logChan, err = f.ApiClient.WatchRuntimeLogs(context.Background(), opts.projectID, opts.serviceID, opts.environmentID, opts.deploymentID) case logTypeBuild: - deployment, exist, err := f.ApiClient.GetLatestDeployment(context.Background(), serviceID, environmentID) - if err != nil { - return nil, fmt.Errorf("failed to get latest deployment: %w", err) - } - if !exist { - return nil, fmt.Errorf("no deployment found for service %s and environment %s", serviceID, environmentID) - } - f.Log.Infof("Deployment ID: %s", deployment.ID) - logs, err := f.ApiClient.GetBuildLogs(context.Background(), deployment.ID) - if err != nil { - return nil, fmt.Errorf("failed to get build logs: %w", err) + deploymentID := opts.deploymentID + if deploymentID == "" { + deployment, exist, e := f.ApiClient.GetLatestDeployment(context.Background(), opts.serviceID, opts.environmentID) + if e != nil { + return fmt.Errorf("failed to get latest deployment: %w", e) + } + if !exist { + return fmt.Errorf("no deployment found for service %s and environment %s", opts.serviceID, opts.environmentID) + } + deploymentID = deployment.ID + f.Log.Infof("Deployment ID: %s", deploymentID) } - return logs, nil + logChan, err = f.ApiClient.WatchBuildLogs(context.Background(), opts.projectID, deploymentID) default: - return nil, fmt.Errorf("unknown log type: %s", logType) + return fmt.Errorf("unknown log type: %s", opts.logType) + } + + if err != nil { + return fmt.Errorf("failed to watch logs: %w", err) + } + + for log := range logChan { + f.Printer.Table(log.Header(), log.Rows()) } + + return nil } func paramCheck(opts *Options) error { if opts.logType != logTypeRuntime && opts.logType != logTypeBuild { return errors.New("log type must be runtime or build") } + if opts.deploymentID != "" { return nil } diff --git a/internal/cmd/project/export/export.go b/internal/cmd/project/export/export.go index e5be823..6669d32 100644 --- a/internal/cmd/project/export/export.go +++ b/internal/cmd/project/export/export.go @@ -49,16 +49,11 @@ func runExport(f *cmdutil.Factory, opts Options) error { } if opts.EnvironmentID == "" { - environments, err := f.ApiClient.ListEnvironments(context.Background(), opts.ProjectID) + envID, err := util.ResolveEnvironmentID(f.ApiClient, opts.ProjectID) if err != nil { - return fmt.Errorf("list environments for project<%s> failed: %w", opts.ProjectID, err) + return err } - - if len(environments) == 0 { - return fmt.Errorf("no environment found in project %s", opts.ProjectID) - } - - opts.EnvironmentID = environments[0].ID + opts.EnvironmentID = envID } exportedTemplate, err := f.ApiClient.ExportProject(context.Background(), opts.ProjectID, opts.EnvironmentID) diff --git a/internal/cmd/service/delete/delete.go b/internal/cmd/service/delete/delete.go index 22cf427..5b919da 100644 --- a/internal/cmd/service/delete/delete.go +++ b/internal/cmd/service/delete/delete.go @@ -67,6 +67,15 @@ func runDeleteInteractive(f *cmdutil.Factory, opts *Options) error { } func runDeleteNonInteractive(f *cmdutil.Factory, opts *Options) error { + if opts.environmentID == "" { + projectID := f.Config.GetContext().GetProject().GetID() + envID, err := util.ResolveEnvironmentID(f.ApiClient, projectID) + if err != nil { + return err + } + opts.environmentID = envID + } + if err := checkParams(opts); err != nil { return err } diff --git a/internal/cmd/service/expose/expose.go b/internal/cmd/service/expose/expose.go index ee5400f..6582355 100644 --- a/internal/cmd/service/expose/expose.go +++ b/internal/cmd/service/expose/expose.go @@ -56,6 +56,15 @@ func runExpose(f *cmdutil.Factory, opts *Options) error { } func runExposeNonInteractive(f *cmdutil.Factory, opts *Options) error { + if opts.environmentID == "" { + projectID := f.Config.GetContext().GetProject().GetID() + envID, err := util.ResolveEnvironmentID(f.ApiClient, projectID) + if err != nil { + return err + } + opts.environmentID = envID + } + err := paramCheck(opts) if err != nil { return err diff --git a/internal/cmd/service/metric/metric.go b/internal/cmd/service/metric/metric.go index 1bff8d2..e1fd2d6 100644 --- a/internal/cmd/service/metric/metric.go +++ b/internal/cmd/service/metric/metric.go @@ -79,6 +79,15 @@ func runMetricInteractive(f *cmdutil.Factory, opts *Options) error { } func runMetricNonInteractive(f *cmdutil.Factory, opts *Options) error { + if opts.environmentID == "" { + projectID := f.Config.GetContext().GetProject().GetID() + envID, err := util.ResolveEnvironmentID(f.ApiClient, projectID) + if err != nil { + return err + } + opts.environmentID = envID + } + if err := paramCheck(opts); err != nil { return err } diff --git a/internal/cmd/service/redeploy/redeploy.go b/internal/cmd/service/redeploy/redeploy.go index bcf6f1f..7471af1 100644 --- a/internal/cmd/service/redeploy/redeploy.go +++ b/internal/cmd/service/redeploy/redeploy.go @@ -68,6 +68,15 @@ func runRedeployInteractive(f *cmdutil.Factory, opts *Options) error { } func runRedeployNonInteractive(f *cmdutil.Factory, opts *Options) error { + if opts.environmentID == "" { + projectID := f.Config.GetContext().GetProject().GetID() + envID, err := util.ResolveEnvironmentID(f.ApiClient, projectID) + if err != nil { + return err + } + opts.environmentID = envID + } + if err := checkParams(opts); err != nil { return err } diff --git a/internal/cmd/service/restart/restart.go b/internal/cmd/service/restart/restart.go index ab1642b..318d404 100644 --- a/internal/cmd/service/restart/restart.go +++ b/internal/cmd/service/restart/restart.go @@ -68,6 +68,15 @@ func runRestartInteractive(f *cmdutil.Factory, opts *Options) error { } func runRestartNonInteractive(f *cmdutil.Factory, opts *Options) error { + if opts.environmentID == "" { + projectID := f.Config.GetContext().GetProject().GetID() + envID, err := util.ResolveEnvironmentID(f.ApiClient, projectID) + if err != nil { + return err + } + opts.environmentID = envID + } + if err := checkParams(opts); err != nil { return err } diff --git a/internal/cmd/service/suspend/suspend.go b/internal/cmd/service/suspend/suspend.go index b752afc..c64e3de 100644 --- a/internal/cmd/service/suspend/suspend.go +++ b/internal/cmd/service/suspend/suspend.go @@ -68,6 +68,15 @@ func runSuspendInteractive(f *cmdutil.Factory, opts *Options) error { } func runSuspendNonInteractive(f *cmdutil.Factory, opts *Options) error { + if opts.environmentID == "" { + projectID := f.Config.GetContext().GetProject().GetID() + envID, err := util.ResolveEnvironmentID(f.ApiClient, projectID) + if err != nil { + return err + } + opts.environmentID = envID + } + if err := checkParams(opts); err != nil { return err } diff --git a/internal/cmd/service/update/tag/tag.go b/internal/cmd/service/update/tag/tag.go index aece41d..97f3727 100644 --- a/internal/cmd/service/update/tag/tag.go +++ b/internal/cmd/service/update/tag/tag.go @@ -83,6 +83,15 @@ func runInteractive(f *cmdutil.Factory, opts *Options) error { } func runNonInteractive(f *cmdutil.Factory, opts *Options) error { + if opts.environmentID == "" { + projectID := f.Config.GetContext().GetProject().GetID() + envID, err := util.ResolveEnvironmentID(f.ApiClient, projectID) + if err != nil { + return err + } + opts.environmentID = envID + } + if err := checkParams(opts); err != nil { return err } diff --git a/internal/util/env.go b/internal/util/env.go index 97c31b9..6c4e52b 100644 --- a/internal/util/env.go +++ b/internal/util/env.go @@ -1,6 +1,12 @@ package util -import "github.com/spf13/cobra" +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + "github.com/zeabur/cli/pkg/api" +) // AddEnvParam todo: support name func AddEnvParam(cmd *cobra.Command, id *string) { @@ -10,3 +16,23 @@ func AddEnvParam(cmd *cobra.Command, id *string) { func AddEnvOfServiceParam(cmd *cobra.Command, id *string) { cmd.Flags().StringVar(id, "env-id", "", "Environment ID of service") } + +// ResolveEnvironmentID resolves the environment ID from the project ID +// by listing environments and returning the first one. +// Every project has exactly one environment since environments are deprecated. +func ResolveEnvironmentID(client api.Client, projectID string) (string, error) { + if projectID == "" { + return "", fmt.Errorf("project ID is required to resolve environment ID; please set project context with `zeabur context set project`") + } + + environments, err := client.ListEnvironments(context.Background(), projectID) + if err != nil { + return "", fmt.Errorf("failed to list environments for project %s: %w", projectID, err) + } + + if len(environments) == 0 { + return "", fmt.Errorf("no environment found for project %s", projectID) + } + + return environments[0].ID, nil +} diff --git a/pkg/api/client.go b/pkg/api/client.go index 65ebf30..d48e017 100644 --- a/pkg/api/client.go +++ b/pkg/api/client.go @@ -41,6 +41,7 @@ func NewGraphQLClientWithToken(token string) *graphql.Client { func NewSubscriptionClient(token string) *graphql.SubscriptionClient { return graphql.NewSubscriptionClient(WSSZeaburGraphQLAPIEndpoint). + WithProtocol(graphql.GraphQLWS). WithConnectionParams(map[string]any{ "authToken": token, }) diff --git a/pkg/api/common.go b/pkg/api/common.go index d4f4107..1af968e 100644 --- a/pkg/api/common.go +++ b/pkg/api/common.go @@ -1,6 +1,8 @@ package api import ( + "strings" + "github.com/zeabur/cli/pkg/model" ) @@ -13,14 +15,26 @@ func (id MapString) GetGraphQLType() string { return `Map` } -// ObjectID is the alias ofskip, limit = normalizePagination(skip, limit) string, it's used to represent the ObjectID defined in GraphQL schema. -type ObjectID string +// objectID represents the ObjectID defined in GraphQL schema. +type objectID string -// GetGraphQLType returns the GraphQL type name of ObjectID. -func (id ObjectID) GetGraphQLType() string { +// GetGraphQLType returns the GraphQL type name of objectID. +func (id objectID) GetGraphQLType() string { return `ObjectID` } +// ObjectID creates an objectID from a string, automatically stripping +// any known prefix (e.g. "service-", "project-", "environment-", "deployment-"). +func ObjectID(id string) objectID { + if idx := strings.LastIndex(id, "-"); idx != -1 { + hex := id[idx+1:] + if len(hex) == 24 { + return objectID(hex) + } + } + return objectID(id) +} + type ServiceTemplate string func (t ServiceTemplate) GetGraphQLType() string { diff --git a/pkg/api/interface.go b/pkg/api/interface.go index e2da934..7529a2c 100644 --- a/pkg/api/interface.go +++ b/pkg/api/interface.go @@ -91,14 +91,11 @@ type ( } LogAPI interface { - // GetRuntimeLogs returns the logs of a service, two cases of parameters: - // 1. only deploymentID - // 2. deploymentID and serviceID - GetRuntimeLogs(ctx context.Context, deploymentID, serviceID, environmentID string) (model.Logs, error) + GetRuntimeLogs(ctx context.Context, serviceID, environmentID, deploymentID string) (model.Logs, error) GetBuildLogs(ctx context.Context, deploymentID string) (model.Logs, error) - WatchRuntimeLogs(ctx context.Context, deploymentID string) (<-chan model.Log, error) - WatchBuildLogs(ctx context.Context, deploymentID string) (<-chan model.Log, error) + WatchRuntimeLogs(ctx context.Context, projectID, serviceID, environmentID, deploymentID string) (<-chan model.Log, error) + WatchBuildLogs(ctx context.Context, projectID, deploymentID string) (<-chan model.Log, error) } GitAPI interface { diff --git a/pkg/api/log.go b/pkg/api/log.go index 8f015db..235e635 100644 --- a/pkg/api/log.go +++ b/pkg/api/log.go @@ -8,37 +8,28 @@ import ( "github.com/zeabur/cli/pkg/model" ) -func (c *client) GetRuntimeLogs(ctx context.Context, deploymentID, serviceID, environmentID string) (model.Logs, error) { - if deploymentID != "" { - return c.getRuntimeLogsByDeploymentID(ctx, deploymentID) - } - - if serviceID != "" && environmentID != "" { - return c.getRuntimeLogsByServiceIDAndEnvironmentID(ctx, serviceID, environmentID) - } - - return nil, fmt.Errorf("invalid arguments") -} - -func (c *client) getRuntimeLogsByDeploymentID(ctx context.Context, deploymentID string) (model.Logs, error) { - var query struct { - Logs model.Logs `graphql:"runtimeLogs(deploymentID: $deploymentID)"` +func (c *client) GetRuntimeLogs(ctx context.Context, serviceID, environmentID, deploymentID string) (model.Logs, error) { + if serviceID == "" { + return nil, fmt.Errorf("serviceID is required for runtime logs") } - err := c.Query(ctx, &query, V{ - "deploymentID": ObjectID(deploymentID), - }) + if deploymentID != "" { + var query struct { + Logs model.Logs `graphql:"runtimeLogs(serviceID: $serviceID, environmentID: $environmentID, deploymentID: $deploymentID)"` + } - fmt.Println("query", query) + err := c.Query(ctx, &query, V{ + "serviceID": ObjectID(serviceID), + "environmentID": ObjectID(environmentID), + "deploymentID": ObjectID(deploymentID), + }) + if err != nil { + return nil, err + } - if err != nil { - return nil, err + return query.Logs, nil } - return query.Logs, nil -} - -func (c *client) getRuntimeLogsByServiceIDAndEnvironmentID(ctx context.Context, serviceID, environmentID string) (model.Logs, error) { var query struct { Logs model.Logs `graphql:"runtimeLogs(serviceID: $serviceID, environmentID: $environmentID)"` } @@ -69,19 +60,27 @@ func (c *client) GetBuildLogs(ctx context.Context, deploymentID string) (model.L return query.Logs, nil } -func (c *client) WatchRuntimeLogs(ctx context.Context, deploymentID string) (<-chan model.Log, error) { - logs := make(chan model.Log, 100) +func (c *client) WatchRuntimeLogs(ctx context.Context, projectID, serviceID, environmentID, deploymentID string) (<-chan model.Log, error) { + if deploymentID != "" { + return c.watchRuntimeLogsWithDeployment(projectID, serviceID, environmentID, deploymentID) + } + return c.watchRuntimeLogs(projectID, serviceID, environmentID) +} +func (c *client) watchRuntimeLogs(projectID, serviceID, environmentID string) (<-chan model.Log, error) { + logs := make(chan model.Log, 100) subClient := c.sub type subscription struct { - Log model.Log `graphql:"runtimeLogReceived(deploymentID: $deploymentID)"` + Log model.Log `graphql:"runtimeLogReceived(projectID: $projectID, serviceID: $serviceID, environmentID: $environmentID)"` } sub := subscription{} _, err := subClient.Subscribe(&sub, V{ - "deploymentID": ObjectID(deploymentID), + "projectID": ObjectID(projectID), + "serviceID": ObjectID(serviceID), + "environmentID": ObjectID(environmentID), }, func(dataValue []byte, errValue error) error { if errValue != nil { fmt.Println(errValue) @@ -93,7 +92,6 @@ func (c *client) WatchRuntimeLogs(ctx context.Context, deploymentID string) (<-c } data := subscription{} - err := jsonutil.UnmarshalGraphQL(dataValue, &data) if err != nil { fmt.Println(err) @@ -101,7 +99,6 @@ func (c *client) WatchRuntimeLogs(ctx context.Context, deploymentID string) (<-c } logs <- data.Log - return nil }) if err != nil { @@ -109,27 +106,72 @@ func (c *client) WatchRuntimeLogs(ctx context.Context, deploymentID string) (<-c } go func() { - err := subClient.Run() + defer close(logs) + _ = subClient.Run() + }() + + return logs, nil +} + +func (c *client) watchRuntimeLogsWithDeployment(projectID, serviceID, environmentID, deploymentID string) (<-chan model.Log, error) { + logs := make(chan model.Log, 100) + subClient := c.sub + + type subscription struct { + Log model.Log `graphql:"runtimeLogReceived(projectID: $projectID, serviceID: $serviceID, environmentID: $environmentID, deploymentID: $deploymentID)"` + } + + sub := subscription{} + + _, err := subClient.Subscribe(&sub, V{ + "projectID": ObjectID(projectID), + "serviceID": ObjectID(serviceID), + "environmentID": ObjectID(environmentID), + "deploymentID": ObjectID(deploymentID), + }, func(dataValue []byte, errValue error) error { + if errValue != nil { + fmt.Println(errValue) + return nil + } + + if dataValue == nil { + return nil + } + + data := subscription{} + err := jsonutil.UnmarshalGraphQL(dataValue, &data) if err != nil { - return + fmt.Println(err) + return nil } + + logs <- data.Log + return nil + }) + if err != nil { + return nil, err + } + + go func() { + defer close(logs) + _ = subClient.Run() }() return logs, nil } -func (c *client) WatchBuildLogs(ctx context.Context, deploymentID string) (<-chan model.Log, error) { +func (c *client) WatchBuildLogs(ctx context.Context, projectID, deploymentID string) (<-chan model.Log, error) { logs := make(chan model.Log, 100) - subClient := c.sub type subscription struct { - Log model.Log `graphql:"buildLogReceived(deploymentID: $deploymentID)"` + Log model.Log `graphql:"buildLogReceived(projectID: $projectID, deploymentID: $deploymentID)"` } sub := subscription{} _, err := subClient.Subscribe(&sub, V{ + "projectID": ObjectID(projectID), "deploymentID": ObjectID(deploymentID), }, func(dataValue []byte, errValue error) error { if errValue != nil { @@ -142,7 +184,6 @@ func (c *client) WatchBuildLogs(ctx context.Context, deploymentID string) (<-cha } data := subscription{} - err := jsonutil.UnmarshalGraphQL(dataValue, &data) if err != nil { fmt.Println(err) @@ -150,7 +191,6 @@ func (c *client) WatchBuildLogs(ctx context.Context, deploymentID string) (<-cha } logs <- data.Log - return nil }) if err != nil { @@ -158,10 +198,8 @@ func (c *client) WatchBuildLogs(ctx context.Context, deploymentID string) (<-cha } go func() { - err := subClient.Run() - if err != nil { - return - } + defer close(logs) + _ = subClient.Run() }() return logs, nil