| 
 | 1 | +package describers  | 
 | 2 | + | 
 | 3 | +import (  | 
 | 4 | +	"context"  | 
 | 5 | +	"encoding/json"  | 
 | 6 | +	"fmt"  | 
 | 7 | +	"github.com/opengovern/og-describer-github/discovery/pkg/models"  | 
 | 8 | +	model "github.com/opengovern/og-describer-github/discovery/provider"  | 
 | 9 | +	resilientbridge "github.com/opengovern/resilient-bridge"  | 
 | 10 | +	"github.com/opengovern/resilient-bridge/adapters"  | 
 | 11 | +	"strconv"  | 
 | 12 | +	"time"  | 
 | 13 | +)  | 
 | 14 | + | 
 | 15 | +func ListOrganizationApps(ctx context.Context,  | 
 | 16 | +	githubClient model.GitHubClient,  | 
 | 17 | +	organizationName string,  | 
 | 18 | +	stream *models.StreamSender) ([]models.Resource, error) {  | 
 | 19 | +	sdk := resilientbridge.NewResilientBridge()  | 
 | 20 | +	sdk.RegisterProvider("github", adapters.NewGitHubAdapter(githubClient.Token), &resilientbridge.ProviderConfig{  | 
 | 21 | +		UseProviderLimits: true,  | 
 | 22 | +		MaxRetries:        3,  | 
 | 23 | +		BaseBackoff:       0,  | 
 | 24 | +	})  | 
 | 25 | + | 
 | 26 | +	var values []models.Resource  | 
 | 27 | + | 
 | 28 | +	endpoint := fmt.Sprintf("/orgs/%s/installations", organizationName)  | 
 | 29 | +	req := &resilientbridge.NormalizedRequest{  | 
 | 30 | +		Method:   "GET",  | 
 | 31 | +		Endpoint: endpoint,  | 
 | 32 | +		Headers:  map[string]string{"Accept": "application/vnd.github+json"},  | 
 | 33 | +	}  | 
 | 34 | + | 
 | 35 | +	resp, err := sdk.Request("github", req)  | 
 | 36 | +	if err != nil {  | 
 | 37 | +		return nil, fmt.Errorf("error fetching repos: %w", err)  | 
 | 38 | +	}  | 
 | 39 | +	if resp.StatusCode != 200 {  | 
 | 40 | +		return nil, fmt.Errorf("HTTP error %d: %s", resp.StatusCode, string(resp.Data))  | 
 | 41 | +	}  | 
 | 42 | + | 
 | 43 | +	var appsResponse OrganizationAppsResponse  | 
 | 44 | +	if err := json.Unmarshal(resp.Data, &appsResponse); err != nil {  | 
 | 45 | +		return nil, fmt.Errorf("error decoding repos list: %w", err)  | 
 | 46 | +	}  | 
 | 47 | + | 
 | 48 | +	for _, app := range appsResponse.Installations {  | 
 | 49 | +		account := model.Account{  | 
 | 50 | +			Login:             app.Account.Login,  | 
 | 51 | +			ID:                app.Account.ID,  | 
 | 52 | +			NodeID:            app.Account.NodeID,  | 
 | 53 | +			AvatarURL:         app.Account.AvatarURL,  | 
 | 54 | +			GravatarID:        app.Account.GravatarID,  | 
 | 55 | +			URL:               app.Account.URL,  | 
 | 56 | +			HTMLURL:           app.Account.HTMLURL,  | 
 | 57 | +			FollowersURL:      app.Account.FollowersURL,  | 
 | 58 | +			FollowingURL:      app.Account.FollowingURL,  | 
 | 59 | +			GistsURL:          app.Account.GistsURL,  | 
 | 60 | +			StarredURL:        app.Account.StarredURL,  | 
 | 61 | +			SubscriptionsURL:  app.Account.SubscriptionsURL,  | 
 | 62 | +			OrganizationsURL:  app.Account.OrganizationsURL,  | 
 | 63 | +			ReposURL:          app.Account.ReposURL,  | 
 | 64 | +			EventsURL:         app.Account.EventsURL,  | 
 | 65 | +			ReceivedEventsURL: app.Account.ReceivedEventsURL,  | 
 | 66 | +			Type:              app.Account.Type,  | 
 | 67 | +			UserViewType:      app.Account.UserViewType,  | 
 | 68 | +			SiteAdmin:         app.Account.SiteAdmin,  | 
 | 69 | +		}  | 
 | 70 | +		var suspendedBy *model.Account  | 
 | 71 | +		if app.SuspendedBy != nil {  | 
 | 72 | +			suspendedBy = &model.Account{  | 
 | 73 | +				Login:             app.SuspendedBy.Login,  | 
 | 74 | +				ID:                app.SuspendedBy.ID,  | 
 | 75 | +				NodeID:            app.SuspendedBy.NodeID,  | 
 | 76 | +				AvatarURL:         app.SuspendedBy.AvatarURL,  | 
 | 77 | +				GravatarID:        app.SuspendedBy.GravatarID,  | 
 | 78 | +				URL:               app.SuspendedBy.URL,  | 
 | 79 | +				HTMLURL:           app.SuspendedBy.HTMLURL,  | 
 | 80 | +				FollowersURL:      app.SuspendedBy.FollowersURL,  | 
 | 81 | +				FollowingURL:      app.SuspendedBy.FollowingURL,  | 
 | 82 | +				GistsURL:          app.SuspendedBy.GistsURL,  | 
 | 83 | +				StarredURL:        app.SuspendedBy.StarredURL,  | 
 | 84 | +				SubscriptionsURL:  app.SuspendedBy.SubscriptionsURL,  | 
 | 85 | +				OrganizationsURL:  app.SuspendedBy.OrganizationsURL,  | 
 | 86 | +				ReposURL:          app.SuspendedBy.ReposURL,  | 
 | 87 | +				EventsURL:         app.SuspendedBy.EventsURL,  | 
 | 88 | +				ReceivedEventsURL: app.SuspendedBy.ReceivedEventsURL,  | 
 | 89 | +				Type:              app.SuspendedBy.Type,  | 
 | 90 | +				UserViewType:      app.SuspendedBy.UserViewType,  | 
 | 91 | +				SiteAdmin:         app.SuspendedBy.SiteAdmin,  | 
 | 92 | +			}  | 
 | 93 | +		}  | 
 | 94 | +		value := models.Resource{  | 
 | 95 | +			ID:   strconv.Itoa(int(app.ID)),  | 
 | 96 | +			Name: app.AppSlug,  | 
 | 97 | +			Description: model.OrganizationAppDescription{  | 
 | 98 | +				ID:                     app.ID,  | 
 | 99 | +				ClientID:               app.ClientID,  | 
 | 100 | +				Account:                account,  | 
 | 101 | +				RepositorySelection:    app.RepositorySelection,  | 
 | 102 | +				AccessTokensURL:        app.AccessTokensURL,  | 
 | 103 | +				RepositoriesURL:        app.RepositoriesURL,  | 
 | 104 | +				HTMLURL:                app.HTMLURL,  | 
 | 105 | +				AppID:                  app.AppID,  | 
 | 106 | +				AppSlug:                app.AppSlug,  | 
 | 107 | +				TargetID:               app.TargetID,  | 
 | 108 | +				TargetType:             app.TargetType,  | 
 | 109 | +				Permissions:            app.Permissions,  | 
 | 110 | +				Events:                 app.Events,  | 
 | 111 | +				CreatedAt:              app.CreatedAt,  | 
 | 112 | +				UpdatedAt:              app.UpdatedAt,  | 
 | 113 | +				SingleFileName:         app.SingleFileName,  | 
 | 114 | +				HasMultipleSingleFiles: app.HasMultipleSingleFiles,  | 
 | 115 | +				SingleFilePaths:        app.SingleFilePaths,  | 
 | 116 | +				SuspendedBy:            suspendedBy,  | 
 | 117 | +				SuspendedAt:            app.SuspendedAt,  | 
 | 118 | +			},  | 
 | 119 | +		}  | 
 | 120 | +		if stream != nil {  | 
 | 121 | +			if err := (*stream)(value); err != nil {  | 
 | 122 | +				return nil, err  | 
 | 123 | +			}  | 
 | 124 | +		} else {  | 
 | 125 | +			values = append(values, value)  | 
 | 126 | +		}  | 
 | 127 | +	}  | 
 | 128 | + | 
 | 129 | +	return values, nil  | 
 | 130 | +}  | 
 | 131 | + | 
 | 132 | +type OrganizationAppsResponse struct {  | 
 | 133 | +	Installations []OrganizationApp `json:"installations"`  | 
 | 134 | +}  | 
 | 135 | + | 
 | 136 | +type OrganizationApp struct {  | 
 | 137 | +	ID                     int64             `json:"id"`  | 
 | 138 | +	ClientID               string            `json:"client_id"`  | 
 | 139 | +	Account                AccountJSON       `json:"account"`  | 
 | 140 | +	RepositorySelection    string            `json:"repository_selection"`  | 
 | 141 | +	AccessTokensURL        string            `json:"access_tokens_url"`  | 
 | 142 | +	RepositoriesURL        string            `json:"repositories_url"`  | 
 | 143 | +	HTMLURL                string            `json:"html_url"`  | 
 | 144 | +	AppID                  int               `json:"app_id"`  | 
 | 145 | +	AppSlug                string            `json:"app_slug"`  | 
 | 146 | +	TargetID               int64             `json:"target_id"`  | 
 | 147 | +	TargetType             string            `json:"target_type"`  | 
 | 148 | +	Permissions            map[string]string `json:"permissions"`  | 
 | 149 | +	Events                 []string          `json:"events"`  | 
 | 150 | +	CreatedAt              time.Time         `json:"created_at"`  | 
 | 151 | +	UpdatedAt              time.Time         `json:"updated_at"`  | 
 | 152 | +	SingleFileName         *string           `json:"single_file_name"`  | 
 | 153 | +	HasMultipleSingleFiles bool              `json:"has_multiple_single_files"`  | 
 | 154 | +	SingleFilePaths        []string          `json:"single_file_paths"`  | 
 | 155 | +	SuspendedBy            *AccountJSON      `json:"suspended_by"`  | 
 | 156 | +	SuspendedAt            *time.Time        `json:"suspended_at"`  | 
 | 157 | +}  | 
 | 158 | + | 
 | 159 | +type AccountJSON struct {  | 
 | 160 | +	Login             string `json:"login"`  | 
 | 161 | +	ID                int64  `json:"id"`  | 
 | 162 | +	NodeID            string `json:"node_id"`  | 
 | 163 | +	AvatarURL         string `json:"avatar_url"`  | 
 | 164 | +	GravatarID        string `json:"gravatar_id"`  | 
 | 165 | +	URL               string `json:"url"`  | 
 | 166 | +	HTMLURL           string `json:"html_url"`  | 
 | 167 | +	FollowersURL      string `json:"followers_url"`  | 
 | 168 | +	FollowingURL      string `json:"following_url"`  | 
 | 169 | +	GistsURL          string `json:"gists_url"`  | 
 | 170 | +	StarredURL        string `json:"starred_url"`  | 
 | 171 | +	SubscriptionsURL  string `json:"subscriptions_url"`  | 
 | 172 | +	OrganizationsURL  string `json:"organizations_url"`  | 
 | 173 | +	ReposURL          string `json:"repos_url"`  | 
 | 174 | +	EventsURL         string `json:"events_url"`  | 
 | 175 | +	ReceivedEventsURL string `json:"received_events_url"`  | 
 | 176 | +	Type              string `json:"type"`  | 
 | 177 | +	UserViewType      string `json:"user_view_type"`  | 
 | 178 | +	SiteAdmin         bool   `json:"site_admin"`  | 
 | 179 | +}  | 
0 commit comments