package implcloudintegration import ( "context" "net/http" "time" "github.com/SigNoz/signoz/pkg/http/binding" "github.com/SigNoz/signoz/pkg/http/render" "github.com/SigNoz/signoz/pkg/modules/cloudintegration" "github.com/SigNoz/signoz/pkg/types/authtypes" "github.com/SigNoz/signoz/pkg/types/cloudintegrationtypes" "github.com/SigNoz/signoz/pkg/valuer" "github.com/gorilla/mux" ) type handler struct { module cloudintegration.Module } func NewHandler(module cloudintegration.Module) cloudintegration.Handler { return &handler{ module: module, } } func (handler *handler) GetConnectionCredentials(rw http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) defer cancel() claims, err := authtypes.ClaimsFromContext(ctx) if err != nil { render.Error(rw, err) return } provider, err := cloudintegrationtypes.NewCloudProvider(mux.Vars(r)["cloud_provider"]) if err != nil { render.Error(rw, err) return } creds, err := handler.module.GetConnectionCredentials(ctx, valuer.MustNewUUID(claims.OrgID), provider) if err != nil { render.Error(rw, err) return } render.Success(rw, http.StatusOK, creds) } func (handler *handler) CreateAccount(rw http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) defer cancel() claims, err := authtypes.ClaimsFromContext(ctx) if err != nil { render.Error(rw, err) return } provider, err := cloudintegrationtypes.NewCloudProvider(mux.Vars(r)["cloud_provider"]) if err != nil { render.Error(rw, err) return } postableAccount := new(cloudintegrationtypes.PostableAccount) err = binding.JSON.BindBody(r.Body, postableAccount) if err != nil { render.Error(rw, err) return } accountConfig, err := cloudintegrationtypes.NewAccountConfigFromPostable(provider, postableAccount.Config) if err != nil { render.Error(rw, err) return } account := cloudintegrationtypes.NewAccount(valuer.MustNewUUID(claims.OrgID), provider, accountConfig) err = handler.module.CreateAccount(ctx, account) if err != nil { render.Error(rw, err) return } connectionArtifact, err := handler.module.GetConnectionArtifact(ctx, account, postableAccount) if err != nil { render.Error(rw, err) return } render.Success(rw, http.StatusCreated, cloudintegrationtypes.NewGettableAccountWithConnectionArtifact(account, connectionArtifact)) } func (handler *handler) GetAccount(rw http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) defer cancel() claims, err := authtypes.ClaimsFromContext(ctx) if err != nil { render.Error(rw, err) return } provider, err := cloudintegrationtypes.NewCloudProvider(mux.Vars(r)["cloud_provider"]) if err != nil { render.Error(rw, err) return } accountID, err := valuer.NewUUID(mux.Vars(r)["id"]) if err != nil { render.Error(rw, err) return } account, err := handler.module.GetAccount(ctx, valuer.MustNewUUID(claims.OrgID), accountID, provider) if err != nil { render.Error(rw, err) return } render.Success(rw, http.StatusOK, account) } func (handler *handler) ListAccounts(rw http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) defer cancel() claims, err := authtypes.ClaimsFromContext(ctx) if err != nil { render.Error(rw, err) return } provider, err := cloudintegrationtypes.NewCloudProvider(mux.Vars(r)["cloud_provider"]) if err != nil { render.Error(rw, err) return } accounts, err := handler.module.ListAccounts(ctx, valuer.MustNewUUID(claims.OrgID), provider) if err != nil { render.Error(rw, err) return } render.Success(rw, http.StatusOK, cloudintegrationtypes.NewGettableAccounts(accounts)) } func (handler *handler) UpdateAccount(rw http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) defer cancel() claims, err := authtypes.ClaimsFromContext(ctx) if err != nil { render.Error(rw, err) return } provider, err := cloudintegrationtypes.NewCloudProvider(mux.Vars(r)["cloud_provider"]) if err != nil { render.Error(rw, err) return } cloudIntegrationID, err := valuer.NewUUID(mux.Vars(r)["id"]) if err != nil { render.Error(rw, err) return } req := new(cloudintegrationtypes.UpdatableAccount) if err := binding.JSON.BindBody(r.Body, req); err != nil { render.Error(rw, err) return } account, err := handler.module.GetConnectedAccount(ctx, valuer.MustNewUUID(claims.OrgID), cloudIntegrationID, provider) if err != nil { render.Error(rw, err) return } accountConfig, err := cloudintegrationtypes.NewAccountConfigFromUpdatable(provider, req) if err != nil { render.Error(rw, err) return } if err := account.Update(provider, accountConfig); err != nil { render.Error(rw, err) return } err = handler.module.UpdateAccount(ctx, account) if err != nil { render.Error(rw, err) return } render.Success(rw, http.StatusNoContent, nil) } func (handler *handler) DisconnectAccount(rw http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) defer cancel() claims, err := authtypes.ClaimsFromContext(ctx) if err != nil { render.Error(rw, err) return } provider, err := cloudintegrationtypes.NewCloudProvider(mux.Vars(r)["cloud_provider"]) if err != nil { render.Error(rw, err) return } cloudIntegrationID, err := valuer.NewUUID(mux.Vars(r)["id"]) if err != nil { render.Error(rw, err) return } err = handler.module.DisconnectAccount(ctx, valuer.MustNewUUID(claims.OrgID), cloudIntegrationID, provider) if err != nil { render.Error(rw, err) return } render.Success(rw, http.StatusNoContent, nil) } func (handler *handler) ListServicesMetadata(rw http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) defer cancel() claims, err := authtypes.ClaimsFromContext(ctx) if err != nil { render.Error(rw, err) return } provider, err := cloudintegrationtypes.NewCloudProvider(mux.Vars(r)["cloud_provider"]) if err != nil { render.Error(rw, err) return } queryParams := new(cloudintegrationtypes.ListServicesMetadataParams) if err := binding.Query.BindQuery(r.URL.Query(), queryParams); err != nil { render.Error(rw, err) return } orgID := valuer.MustNewUUID(claims.OrgID) // check if integration account exists and is not removed. if !queryParams.CloudIntegrationID.IsZero() { _, err := handler.module.GetConnectedAccount(ctx, orgID, queryParams.CloudIntegrationID, provider) if err != nil { render.Error(rw, err) return } } services, err := handler.module.ListServicesMetadata(ctx, orgID, provider, queryParams.CloudIntegrationID) if err != nil { render.Error(rw, err) return } render.Success(rw, http.StatusOK, cloudintegrationtypes.NewGettableServicesMetadata(services)) } func (handler *handler) GetService(rw http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) defer cancel() claims, err := authtypes.ClaimsFromContext(ctx) if err != nil { render.Error(rw, err) return } provider, err := cloudintegrationtypes.NewCloudProvider(mux.Vars(r)["cloud_provider"]) if err != nil { render.Error(rw, err) return } serviceID, err := cloudintegrationtypes.NewServiceID(provider, mux.Vars(r)["service_id"]) if err != nil { render.Error(rw, err) return } queryParams := new(cloudintegrationtypes.GetServiceParams) if err := binding.Query.BindQuery(r.URL.Query(), queryParams); err != nil { render.Error(rw, err) return } // check if integration account exists and is not removed. if !queryParams.CloudIntegrationID.IsZero() { _, err := handler.module.GetConnectedAccount(ctx, valuer.MustNewUUID(claims.OrgID), queryParams.CloudIntegrationID, provider) if err != nil { render.Error(rw, err) return } } svc, err := handler.module.GetService(ctx, valuer.MustNewUUID(claims.OrgID), serviceID, provider, queryParams.CloudIntegrationID) if err != nil { render.Error(rw, err) return } render.Success(rw, http.StatusOK, svc) } func (handler *handler) UpdateService(rw http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) defer cancel() claims, err := authtypes.ClaimsFromContext(ctx) if err != nil { render.Error(rw, err) return } provider, err := cloudintegrationtypes.NewCloudProvider(mux.Vars(r)["cloud_provider"]) if err != nil { render.Error(rw, err) return } serviceID, err := cloudintegrationtypes.NewServiceID(provider, mux.Vars(r)["service_id"]) if err != nil { render.Error(rw, err) return } req := new(cloudintegrationtypes.UpdatableService) if err := binding.JSON.BindBody(r.Body, req); err != nil { render.Error(rw, err) return } cloudIntegrationID, err := valuer.NewUUID(mux.Vars(r)["id"]) if err != nil { render.Error(rw, err) return } orgID := valuer.MustNewUUID(claims.OrgID) // check if integration account exists and is not removed. _, err = handler.module.GetConnectedAccount(ctx, orgID, cloudIntegrationID, provider) if err != nil { render.Error(rw, err) return } svc, err := handler.module.GetService(ctx, orgID, serviceID, provider, cloudIntegrationID) if err != nil { render.Error(rw, err) return } // update or create service if svc.CloudIntegrationService == nil { cloudIntegrationService := cloudintegrationtypes.NewCloudIntegrationService(serviceID, cloudIntegrationID, req.Config) err = handler.module.CreateService(ctx, orgID, cloudIntegrationService, provider) } else { err = svc.CloudIntegrationService.Update(provider, serviceID, req.Config) if err != nil { render.Error(rw, err) return } err = handler.module.UpdateService(ctx, orgID, svc.CloudIntegrationService, provider) } if err != nil { render.Error(rw, err) return } render.Success(rw, http.StatusNoContent, nil) } func (handler *handler) AgentCheckIn(rw http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) defer cancel() claims, err := authtypes.ClaimsFromContext(ctx) if err != nil { render.Error(rw, err) return } provider, err := cloudintegrationtypes.NewCloudProvider(mux.Vars(r)["cloud_provider"]) if err != nil { render.Error(rw, err) return } req := new(cloudintegrationtypes.PostableAgentCheckIn) if err := binding.JSON.BindBody(r.Body, req); err != nil { render.Error(rw, err) return } // Map old fields → new fields for backward compatibility with old agents // Old agents send account_id (=> cloudIntegrationId) and cloud_account_id (=> providerAccountId) if req.ID != "" { id, err := valuer.NewUUID(req.ID) if err != nil { render.Error(rw, err) return } req.CloudIntegrationID = id req.ProviderAccountID = req.AccountID } resp, err := handler.module.AgentCheckIn(ctx, valuer.MustNewUUID(claims.OrgID), provider, &req.AgentCheckInRequest) if err != nil { render.Error(rw, err) return } render.Success(rw, http.StatusOK, cloudintegrationtypes.NewGettableAgentCheckIn(provider, resp)) }