fix: use installedLocations from bulk token response instead of /locations/search
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
GHL includes installedLocations[] in the company-level token response for bulk installs. Use those IDs directly to avoid calling /locations/search, which requires locations.readonly scope that GHL doesn't grant. Falls back to /locations/search only when the list is absent. Also adds raw_body and installed_locations fields to token response debug logging. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
3d1e80cd86
commit
3863e8f0cd
@ -227,31 +227,48 @@ func (h *OAuthHandler) postToken(ctx context.Context, data url.Values) (*TokenRe
|
||||
if err := json.Unmarshal(body, &tokenResp); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse token response: %w", err)
|
||||
}
|
||||
slog.Info("ghl token response fields", "location_id", tokenResp.LocationID, "company_id", tokenResp.CompanyID, "user_type", tokenResp.UserType)
|
||||
slog.Info("ghl token response fields",
|
||||
"location_id", tokenResp.LocationID,
|
||||
"company_id", tokenResp.CompanyID,
|
||||
"user_type", tokenResp.UserType,
|
||||
"installed_locations", tokenResp.InstalledLocations,
|
||||
"raw_body", string(body),
|
||||
)
|
||||
return &tokenResp, nil
|
||||
}
|
||||
|
||||
// installAllLocations fetches all locations for the company and exchanges the company token
|
||||
// for a per-location token for each, storing them all. Returns the count installed.
|
||||
// installAllLocations exchanges the company token for per-location tokens and stores them.
|
||||
// It first tries to use the InstalledLocations list from the token response (provided by GHL
|
||||
// for bulk installs). Falls back to GET /locations/search if that list is empty.
|
||||
func (h *OAuthHandler) installAllLocations(ctx context.Context, companyToken *TokenResponse) (int, error) {
|
||||
locations, err := h.getCompanyLocations(ctx, companyToken.AccessToken, companyToken.CompanyID)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("list locations: %w", err)
|
||||
}
|
||||
if len(locations) == 0 {
|
||||
return 0, fmt.Errorf("no locations found for company %s", companyToken.CompanyID)
|
||||
// Build location ID list from token response when GHL provides it (bulk install path).
|
||||
locationIDs := companyToken.InstalledLocations
|
||||
if len(locationIDs) == 0 {
|
||||
slog.Info("ghl bulk install: no installedLocations in token, falling back to locations search", "company_id", companyToken.CompanyID)
|
||||
locations, err := h.getCompanyLocations(ctx, companyToken.AccessToken, companyToken.CompanyID)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("list locations: %w", err)
|
||||
}
|
||||
if len(locations) == 0 {
|
||||
return 0, fmt.Errorf("no locations found for company %s", companyToken.CompanyID)
|
||||
}
|
||||
for _, loc := range locations {
|
||||
locationIDs = append(locationIDs, loc.ID)
|
||||
}
|
||||
} else {
|
||||
slog.Info("ghl bulk install: using installedLocations from token", "company_id", companyToken.CompanyID, "count", len(locationIDs))
|
||||
}
|
||||
|
||||
installed := 0
|
||||
for _, loc := range locations {
|
||||
locToken, err := h.exchangeForLocationToken(ctx, companyToken.AccessToken, companyToken.CompanyID, loc.ID)
|
||||
for _, locID := range locationIDs {
|
||||
locToken, err := h.exchangeForLocationToken(ctx, companyToken.AccessToken, companyToken.CompanyID, locID)
|
||||
if err != nil {
|
||||
slog.Warn("ghl location token exchange failed", "location_id", loc.ID, "location_name", loc.Name, "err", err)
|
||||
slog.Warn("ghl location token exchange failed", "location_id", locID, "err", err)
|
||||
continue
|
||||
}
|
||||
expiresAt := time.Now().Add(time.Duration(locToken.ExpiresIn) * time.Second)
|
||||
record := &store.TokenRecord{
|
||||
LocationID: loc.ID,
|
||||
LocationID: locID,
|
||||
CompanyID: companyToken.CompanyID,
|
||||
AccessToken: locToken.AccessToken,
|
||||
RefreshToken: locToken.RefreshToken,
|
||||
@ -260,10 +277,10 @@ func (h *OAuthHandler) installAllLocations(ctx context.Context, companyToken *To
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
if err := h.store.SaveToken(ctx, record); err != nil {
|
||||
slog.Warn("ghl location token save failed", "location_id", loc.ID, "err", err)
|
||||
slog.Warn("ghl location token save failed", "location_id", locID, "err", err)
|
||||
continue
|
||||
}
|
||||
slog.Info("ghl location installed", "location_id", loc.ID, "location_name", loc.Name)
|
||||
slog.Info("ghl location installed", "location_id", locID)
|
||||
installed++
|
||||
}
|
||||
return installed, nil
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
package ghl
|
||||
|
||||
type TokenResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
TokenType string `json:"token_type"`
|
||||
LocationID string `json:"locationId"`
|
||||
CompanyID string `json:"companyId"`
|
||||
UserType string `json:"userType"`
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
TokenType string `json:"token_type"`
|
||||
LocationID string `json:"locationId"`
|
||||
CompanyID string `json:"companyId"`
|
||||
UserType string `json:"userType"`
|
||||
InstalledLocations []string `json:"installedLocations"` // populated on bulk company installs
|
||||
}
|
||||
|
||||
type LocationInfo struct {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user