github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/pkg/asc/asc.go (about) 1 package asc 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "net/http" 9 url2 "net/url" 10 "strconv" 11 "strings" 12 "time" 13 14 piperHttp "github.com/SAP/jenkins-library/pkg/http" 15 "github.com/SAP/jenkins-library/pkg/log" 16 "github.com/pkg/errors" 17 "github.com/sirupsen/logrus" 18 ) 19 20 type App struct { 21 AppId int `json:"app_id"` 22 AppName string `json:"app_name"` 23 BundleId string `json:"bundle_id"` 24 JamfId string `json:"jamf_id"` 25 } 26 27 type JamfAppInformationResponse struct { 28 MobileDeviceApplication JamfMobileDeviceApplication `json:"mobile_device_application"` 29 } 30 31 type JamfMobileDeviceApplication struct { 32 General JamfMobileDeviceApplicationGeneral `json:"general"` 33 } 34 35 type JamfMobileDeviceApplicationGeneral struct { 36 Id int `json:"id"` 37 } 38 39 type CreateReleaseResponse struct { 40 Status string `json:"status"` 41 Message string `json:"message"` 42 LastID int `json:"lastID"` 43 Data Release `json:"data"` 44 } 45 46 type Release struct { 47 ReleaseID int `json:"release_id"` 48 AppID int `json:"app_id"` 49 Version string `json:"version"` 50 Description string `json:"description"` 51 ReleaseDate time.Time `json:"release_date"` 52 SortOrder any `json:"sort_order"` 53 Visible bool `json:"visible"` 54 Created time.Time `json:"created"` 55 FileMetadata any `json:"file_metadata"` 56 } 57 58 // SystemInstance is the client communicating with the ASC backend 59 type SystemInstance struct { 60 serverURL string 61 token string 62 client *piperHttp.Client 63 logger *logrus.Entry 64 } 65 66 type System interface { 67 GetAppById(appId string) (App, error) 68 CreateRelease(ascAppId int, version string, description string, releaseDate string, visible bool) (CreateReleaseResponse, error) 69 GetJamfAppInfo(bundleId string, jamfTargetSystem string) (JamfAppInformationResponse, error) 70 UploadIpa(path string, jamfAppId int, jamfTargetSystem string, bundleId string, ascRelease Release) error 71 } 72 73 // NewSystemInstance returns a new ASC client for communicating with the backend 74 func NewSystemInstance(client *piperHttp.Client, serverURL, token string) (*SystemInstance, error) { 75 loggerInstance := log.Entry().WithField("package", "SAP/jenkins-library/pkg/asc") 76 77 if len(serverURL) == 0 { 78 return nil, errors.New("serverUrl is not set but required") 79 } 80 81 if len(token) == 0 { 82 return nil, errors.New("AppToken is not set but required") 83 } 84 85 sys := &SystemInstance{ 86 serverURL: strings.TrimSuffix(serverURL, "/"), 87 token: token, 88 client: client, 89 logger: loggerInstance, 90 } 91 92 log.RegisterSecret(token) 93 94 options := piperHttp.ClientOptions{ 95 Token: fmt.Sprintf("Bearer %s", sys.token), 96 TransportTimeout: time.Second * 15, 97 } 98 sys.client.SetOptions(options) 99 100 return sys, nil 101 } 102 103 func sendRequest(sys *SystemInstance, method, url string, body io.Reader, header http.Header) ([]byte, error) { 104 var requestBody io.Reader 105 if body != nil { 106 closer := io.NopCloser(body) 107 bodyBytes, _ := io.ReadAll(closer) 108 requestBody = bytes.NewBuffer(bodyBytes) 109 defer closer.Close() 110 } 111 response, err := sys.client.SendRequest(method, fmt.Sprintf("%v/%v", sys.serverURL, url), requestBody, header, nil) 112 if err != nil && (response == nil) { 113 sys.logger.Errorf("HTTP request failed with error: %s", err) 114 return nil, err 115 } 116 117 data, _ := io.ReadAll(response.Body) 118 sys.logger.Debugf("Valid response body: %v", string(data)) 119 defer response.Body.Close() 120 return data, nil 121 } 122 123 // GetAppById returns the app addressed by appId from the ASC backend 124 func (sys *SystemInstance) GetAppById(appId string) (App, error) { 125 sys.logger.Debugf("Getting ASC App with ID %v...", appId) 126 var app App 127 128 data, err := sendRequest(sys, http.MethodGet, fmt.Sprintf("api/v1/apps/%v", appId), nil, nil) 129 if err != nil { 130 return app, errors.Wrapf(err, "fetching app %v failed", appId) 131 } 132 133 json.Unmarshal(data, &app) 134 return app, nil 135 } 136 137 // CreateRelease creates a release in ASC 138 func (sys *SystemInstance) CreateRelease(ascAppId int, version string, description string, releaseDate string, visible bool) (CreateReleaseResponse, error) { 139 140 var createReleaseResponse CreateReleaseResponse 141 142 if len(releaseDate) == 0 { 143 currentTime := time.Now() 144 releaseDate = currentTime.Format("01/02/2006") 145 } 146 147 jsonData := map[string]string{ 148 "version": version, 149 "description": description, 150 "release_date": releaseDate, 151 "visible": strconv.FormatBool(visible), 152 } 153 154 jsonValue, err := json.Marshal(jsonData) 155 if err != nil { 156 return createReleaseResponse, errors.Wrap(err, "error marshalling release payload") 157 } 158 159 header := http.Header{} 160 header.Set("Content-Type", "application/json") 161 162 response, err := sendRequest(sys, http.MethodPost, fmt.Sprintf("api/v1/apps/%v/releases", ascAppId), bytes.NewBuffer(jsonValue), header) 163 if err != nil { 164 return createReleaseResponse, errors.Wrapf(err, "creating release") 165 } 166 167 json.Unmarshal(response, &createReleaseResponse) 168 return createReleaseResponse, nil 169 } 170 171 // GetJamfAppInfo fetches information about the app from Jamf 172 func (sys *SystemInstance) GetJamfAppInfo(bundleId string, jamfTargetSystem string) (JamfAppInformationResponse, error) { 173 174 sys.logger.Debugf("Getting Jamf App Info by ID %v from jamf %v system...", bundleId, jamfTargetSystem) 175 var jamfAppInformationResponse JamfAppInformationResponse 176 177 data, err := sendRequest(sys, http.MethodPost, fmt.Sprintf("api/v1/jamf/%v/info?system=%v", bundleId, url2.QueryEscape(jamfTargetSystem)), nil, nil) 178 if err != nil { 179 return jamfAppInformationResponse, errors.Wrapf(err, "fetching jamf %v app info for %v failed", jamfTargetSystem, bundleId) 180 } 181 182 json.Unmarshal(data, &jamfAppInformationResponse) 183 return jamfAppInformationResponse, nil 184 185 } 186 187 // UploadIpa uploads the ipa to ASC and therewith to Jamf 188 func (sys *SystemInstance) UploadIpa(path string, jamfAppId int, jamfTargetSystem string, bundleId string, ascRelease Release) error { 189 190 url := fmt.Sprintf("%v/api/v1/jamf/%v/ipa?app_id=%v&version=%v&system=%v&release_id=%v&bundle_id=%v", sys.serverURL, jamfAppId, ascRelease.AppID, url2.QueryEscape(ascRelease.Version), url2.QueryEscape(jamfTargetSystem), ascRelease.ReleaseID, url2.QueryEscape(bundleId)) 191 _, err := sys.client.UploadFile(url, path, "file", nil, nil, "form") 192 193 if err != nil { 194 return errors.Wrap(err, "failed to upload ipa to asc") 195 } 196 197 return nil 198 }