mirror of
https://github.com/hanzoai/o11y.git
synced 2026-10-15 12:58:33 +00:00
19 reads across three files stop naming gorilla to address a resource by id and go through coretypes.Param: 13 in the query-service handler (rule id, integration id, pipeline version, six funnel ids), 1 in the parser (trace id), 5 in the gateway (key id, limit id). Registration is untouched — this is the read side. Where a site had bound `vars := mux.Vars(r)` only to index it once, the map is gone with it; a handler wants the VALUE, and keeping a map alive to hold one string was the router leaking into the body. The query half stays exactly where it was. ParseSearchTracesParams now reads the trace id from the PATH and the span id from the QUERY through two different calls, which is what they always were. http_handler.go keeps `mux.NewRouter().UseEncodedPath()` and the websocket upgrader: those are the internal tree and its transport, converted as one piece with the other 70 registration files, not here. parser.go drops the gorilla import entirely; gateway/handler.go likewise. Tests: neither package had one that exercised a path segment, so both go green whether the read works or not — this repo has already shipped that defect. Each now drives a REAL router registered at the REAL template. The gateway rows go router -> handler -> Gateway and assert the id the gateway was HANDED, so an empty segment fails on the recorded value rather than on a status code. Under a Param that returns "", all four tests go red; before them, both packages stayed green. Co-authored-by: Hanzo Dev <[email protected]>
527 lines
13 KiB
Go
527 lines
13 KiB
Go
package implcloudintegration
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/hanzoai/o11y/pkg/http/binding"
|
|
"github.com/hanzoai/o11y/pkg/http/render"
|
|
"github.com/hanzoai/o11y/pkg/modules/cloudintegration"
|
|
"github.com/hanzoai/o11y/pkg/types/authtypes"
|
|
"github.com/hanzoai/o11y/pkg/types/cloudintegrationtypes"
|
|
"github.com/hanzoai/o11y/pkg/types/coretypes"
|
|
"github.com/hanzoai/o11y/pkg/valuer"
|
|
)
|
|
|
|
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(coretypes.Param(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(coretypes.Param(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(coretypes.Param(r, "cloud_provider"))
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
accountID, err := valuer.NewUUID(coretypes.Param(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(coretypes.Param(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(coretypes.Param(r, "cloud_provider"))
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
cloudIntegrationID, err := valuer.NewUUID(coretypes.Param(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(coretypes.Param(r, "cloud_provider"))
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
cloudIntegrationID, err := valuer.NewUUID(coretypes.Param(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(coretypes.Param(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) ListAccountServicesMetadata(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(coretypes.Param(r, "cloud_provider"))
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
accountID, err := valuer.NewUUID(coretypes.Param(r, "id"))
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
// check if integration account exists and is not removed.
|
|
_, err = handler.module.GetConnectedAccount(ctx, valuer.MustNewUUID(claims.OrgID), accountID, provider)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
services, err := handler.module.ListServicesMetadata(ctx, valuer.MustNewUUID(claims.OrgID), provider, accountID)
|
|
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(coretypes.Param(r, "cloud_provider"))
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
serviceID, err := cloudintegrationtypes.NewServiceID(provider, coretypes.Param(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) GetAccountService(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(coretypes.Param(r, "cloud_provider"))
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
serviceID, err := cloudintegrationtypes.NewServiceID(provider, coretypes.Param(r, "service_id"))
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
cloudIntegrationID, err := valuer.NewUUID(coretypes.Param(r, "id"))
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
orgID := valuer.MustNewUUID(claims.OrgID)
|
|
|
|
_, 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
|
|
}
|
|
|
|
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(coretypes.Param(r, "cloud_provider"))
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
serviceID, err := cloudintegrationtypes.NewServiceID(provider, coretypes.Param(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(coretypes.Param(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 {
|
|
var cloudIntegrationService *cloudintegrationtypes.CloudIntegrationService
|
|
cloudIntegrationService, err = cloudintegrationtypes.NewCloudIntegrationService(serviceID, cloudIntegrationID, provider, req.Config)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
err = handler.module.CreateService(ctx, orgID, claims.Email, valuer.MustNewUUID(claims.IdentityID()), 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, claims.Email, valuer.MustNewUUID(claims.IdentityID()), svc.CloudIntegrationService, provider)
|
|
}
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
render.Success(rw, http.StatusNoContent, nil)
|
|
}
|
|
|
|
// TODO: Rename AgentCheckIn to just CheckIn.
|
|
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(coretypes.Param(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))
|
|
}
|