github.com/haagen/force@v0.19.6-0.20140911230915-22addd930b34/force.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "crypto/tls" 6 "crypto/x509" 7 "encoding/json" 8 "encoding/pem" 9 "encoding/xml" 10 "errors" 11 "fmt" 12 "io" 13 "io/ioutil" 14 "net" 15 "net/http" 16 "net/url" 17 "runtime" 18 "strings" 19 ) 20 21 const ( 22 ProductionClientId = "3MVG9A2kN3Bn17huXZp1OQhPe8y4_ozAQZZCKxsWbef9GjSnHGOunHSwhnY1BWz_5vHkTL9BeLMriIX5EUKaw" 23 PrereleaseClientId = "3MVG9lKcPoNINVBIRgC7lsz5tIhlg0mtoEqkA9ZjDAwEMbBy43gsnfkzzdTdhFLeNnWS8M4bnRnVv1Qj0k9MD" 24 Mobile1ClientId = "3MVG9Iu66FKeHhIPqCB9VWfYPxjfcb5Ube.v5L81BLhnJtDYVP2nkA.mDPwfm5FTLbvL6aMftfi8w0rL7Dv7f" 25 RedirectUri = "https://force-cli.herokuapp.com/auth/callback" 26 ) 27 28 var CustomEndpoint = `` 29 30 const ( 31 EndpointProduction = iota 32 EndpointTest = iota 33 EndpointPrerelease = iota 34 EndpointMobile1 = iota 35 EndpointCustom = iota 36 ) 37 38 const ( 39 apiVersion = "v31.0" 40 apiVersionNumber = "31.0" 41 ) 42 43 var RootCertificates = ` 44 -----BEGIN CERTIFICATE----- 45 MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG 46 A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz 47 cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 48 MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV 49 BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt 50 YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN 51 ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE 52 BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is 53 I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G 54 CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do 55 lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc 56 AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k 57 -----END CERTIFICATE----- 58 -----BEGIN CERTIFICATE----- 59 MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs 60 MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 61 d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j 62 ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL 63 MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 64 LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug 65 RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm 66 +9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW 67 PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM 68 xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB 69 Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 70 hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg 71 EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF 72 MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA 73 FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec 74 nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z 75 eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF 76 hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 77 Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe 78 vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep 79 +OkuE6N36B9K 80 -----END CERTIFICATE----- 81 -----BEGIN CERTIFICATE----- 82 MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ 83 RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD 84 VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX 85 DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y 86 ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy 87 VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr 88 mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr 89 IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK 90 mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu 91 XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy 92 dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye 93 jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 94 BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 95 DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 96 9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx 97 jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 98 Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz 99 ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS 100 R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp 101 -----END CERTIFICATE----- 102 -----BEGIN CERTIFICATE----- 103 MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ 104 RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD 105 VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX 106 DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y 107 ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy 108 VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr 109 mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr 110 IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK 111 mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu 112 XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy 113 dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye 114 jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 115 BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 116 DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 117 9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx 118 jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 119 Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz 120 ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS 121 R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp 122 -----END CERTIFICATE----- 123 -----BEGIN CERTIFICATE----- 124 MIIEeDCCA2CgAwIBAgIOAQAAAAABNwQKT8CN490wDQYJKoZIhvcNAQEFBQAwUDEX 125 MBUGA1UEChMOQ3liZXJ0cnVzdCBJbmMxNTAzBgNVBAMTLEN5YmVydHJ1c3QgU3Vy 126 ZVNlcnZlciBTdGFuZGFyZCBWYWxpZGF0aW9uIENBMB4XDTEyMDQzMDE2MzI0NloX 127 DTE1MDQzMDE2MzI0NlowgbExCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9y 128 bmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRswGQYDVQQKExJTYWxlc2ZvcmNl 129 LmNvbSBJbmMxFTATBgNVBAsTDEFwcGxpY2F0aW9uczEhMB8GCSqGSIb3DQEJARYS 130 bm9jQHNhbGVzZm9yY2UuY29tMR4wHAYDVQQDDBUqLnNvbWEuc2FsZXNmb3JjZS5j 131 b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHo8bILux1ZYwD1JTW 132 PBE6LTV0hAdILIv06++N0RkhYU0ry69/yAFKWM5SUqt19dk9H3x43uC50tFRUT/l 133 UoP/ztjT8Z47UczjVXSPrRCa/HloiA9zobLNFrsES/atCLHjzoxBLth477iZNnFs 134 sINW8Kz6+v+7G83zzrMs6J4eYzZauNlhvCHBjwotPqtbJEp6MESoEO0XcNJkVLXA 135 2sysfOpZH89P8j+1AMByc/32aauAZqwfmTD1iyGHyguieFdySWMDYL3r3j+uhey8 136 XjxMO5AYRRh6EB4UQ5IlsjyzoAeJg5+q7+dJRhZ3KVr9KJ74UkRLab4NiUFRbXjj 137 TnqjAgMBAAGjge0wgeowHwYDVR0jBBgwFoAUzTqWn65uD0BcHEj4Sy24cQHridow 138 OQYDVR0fBDIwMDAuoCygKoYoaHR0cDovL2NybC5vbW5pcm9vdC5jb20vU3VyZVNl 139 cnZlckcyLmNybDAdBgNVHQ4EFgQUVKhO+ZroJ0ZNcV80wap72/eTqGswCQYDVR0T 140 BAIwADAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUF 141 BwMCMBEGCWCGSAGG+EIBAQQEAwIGwDAgBgNVHREEGTAXghUqLnNvbWEuc2FsZXNm 142 b3JjZS5jb20wDQYJKoZIhvcNAQEFBQADggEBACiMRXtltlPjDdzuTG6B8F6c0AOE 143 nJl5T4Lz4BMc5jvyin3zR1uPrZC7H/VEc6MOzXQK+n1i9xfNGURjTtfpCOdbcmZ9 144 MkkRbu8EJyoO2FM84BdVtCOs5nomE/Py9xqX4mdy38yhjnJywvFa+M4rGDNcVR4W 145 ZOV5H9LlMuEjuVuWYRLSRwu6Uk+QVN/tL9ImiWM1p4cziuizWXtjPqLyaQmOvykY 146 4ihtSnZuel7PqGhBMoFHbuw11CB3S3ap2hzfreeJcYT/019Y5p8DPuFh6BJ3Q85J 147 oo54Un5pgx/wX8L1UaMLMLUSv9d+nuKKLYYg+MW+1+LNNkLP704/Y/GWPvE= 148 -----END CERTIFICATE-----` 149 150 type Force struct { 151 Credentials ForceCredentials 152 Metadata *ForceMetadata 153 Partner *ForcePartner 154 } 155 156 type ForceCredentials struct { 157 AccessToken string 158 Id string 159 InstanceUrl string 160 IssuedAt string 161 Scope string 162 IsCustomEP bool 163 Namespace string 164 ForceEndpoint ForceEndpoint 165 } 166 167 type LoginFault struct { 168 ExceptionCode string `xml:"exceptionCode"` 169 ExceptionMessage string `xml:"exceptionMessage"` 170 } 171 172 type SoapFault struct { 173 FaultCode string `xml:"Body>Fault>faultcode"` 174 FaultString string `xml:"Body>Fault>faultstring"` 175 Detail LoginFault `xml:"Body>Fault>detail>LoginFault"` 176 } 177 178 type GenericForceError struct { 179 Error_Description string 180 Error string 181 } 182 183 type ForceError struct { 184 Message string 185 ErrorCode string 186 } 187 188 type ForceEndpoint int 189 190 type ForceRecord map[string]interface{} 191 192 type ForceSobject map[string]interface{} 193 194 type ForceCreateRecordResult struct { 195 Errors []string 196 Id string 197 Success bool 198 } 199 200 type ForcePasswordStatusResult struct { 201 IsExpired bool 202 } 203 204 type ForcePasswordResetResult struct { 205 NewPassword string 206 } 207 208 type ForceQueryResult struct { 209 Done bool 210 Records []ForceRecord 211 TotalSize int 212 } 213 214 type ForceSobjectsResult struct { 215 Encoding string 216 MaxBatchSize int 217 Sobjects []ForceSobject 218 } 219 220 type Result struct { 221 Id string 222 Success bool 223 Created bool 224 Message string 225 } 226 227 type BatchResult struct { 228 Results []Result 229 } 230 231 type BatchInfo struct { 232 Id string `xml:"id"` 233 JobId string `xml:"jobId"` 234 State string `xml:"state"` 235 CreatedDate string `xml:"createdDate"` 236 SystemModstamp string `xml:"systemModstamp"` 237 NumberRecordsProcessed int `xml:"numberRecordsProcessed"` 238 } 239 240 type JobInfo struct { 241 Id string `xml:"id"` 242 Operation string `xml:"operation"` 243 Object string `xml:"object"` 244 CreatedById string `xml:"createdById"` 245 CreatedDate string `xml:"createdDate"` 246 SystemModStamp string `xml:"systemModstamp"` 247 State string `xml:"state"` 248 ContentType string `xml:"contentType"` 249 ConcurrencyMode string `xml:"concurrencyMode"` 250 NumberBatchesQueued int `xml:"numberBatchesQueued"` 251 NumberBatchesInProgress int `xml:"numberBatchesInProgress"` 252 NumberBatchesCompleted int `xml:"numberBatchesCompleted"` 253 NumberBatchesFailed int `xml:"numberBatchesFailed"` 254 NumberBatchesTotal int `xml:"numberBatchesTotal"` 255 NumberRecordsProcessed int `xml:"numberRecordsProcessed"` 256 NumberRetries int `xml:"numberRetries"` 257 ApiVersion string `xml:"apiVersion"` 258 NumberRecordsFailed int `xml:"numberRecordsFailed"` 259 TotalProcessingTime int `xml:"totalProcessingTime"` 260 ApiActiveProcessingTime int `xml:"apiActiveProcessingTime"` 261 ApexProcessingTime int `xml:"apexProcessingTime"` 262 } 263 264 type AuraDefinitionBundleResult struct { 265 Done bool 266 Records []ForceRecord 267 TotalSize int 268 QueryLocator string 269 Size int 270 EntityTypeName string 271 } 272 273 type AuraDefinitionBundle struct { 274 Id string 275 IsDeleted bool 276 DeveloperName string 277 Language string 278 MasterLabel string 279 NamespacePrefix string 280 CreatedDate string 281 CreatedById string 282 LastModifiedDate string 283 LastModifiedById string 284 SystemModstamp string 285 ApiVersion int 286 Description string 287 } 288 289 type AuraDefinition struct { 290 Id string 291 IsDeleted bool 292 CreatedDate string 293 CreatedById string 294 LastModifiedDate string 295 LastModifiedById string 296 SystemModstamp string 297 AuraDefinitionBundleId string 298 DefType string 299 Format string 300 Source string 301 } 302 303 type ComponentFile struct { 304 FileName string 305 ComponentId string 306 } 307 308 type BundleManifest struct { 309 Name string 310 Id string 311 Files []ComponentFile 312 } 313 314 func NewForce(creds ForceCredentials) (force *Force) { 315 force = new(Force) 316 force.Credentials = creds 317 force.Metadata = NewForceMetadata(force) 318 force.Partner = NewForcePartner(force) 319 return 320 } 321 322 func ForceSoapLogin(endpoint ForceEndpoint, username string, password string) (creds ForceCredentials, err error) { 323 var surl string 324 version := strings.Split(apiVersion, "v")[1] 325 switch endpoint { 326 case EndpointProduction: 327 surl = fmt.Sprintf("https://login.salesforce.com/services/Soap/u/%s", version) 328 case EndpointTest: 329 surl = fmt.Sprintf("https://test.salesforce.com/services/Soap/u/%s", version) 330 case EndpointPrerelease: 331 surl = fmt.Sprintf("https://prerelna1.pre.salesforce.com/services/Soap/u/%s", version) 332 case EndpointCustom: 333 surl = fmt.Sprintf("%s/services/Soap/u/%s", CustomEndpoint, version) 334 default: 335 ErrorAndExit("no such endpoint type") 336 } 337 338 soap := NewSoap(surl, "", "") 339 response, err := soap.ExecuteLogin(username, password) 340 var result struct { 341 SessionId string `xml:"Body>loginResponse>result>sessionId"` 342 Id string `xml:"Body>loginResponse>result>userId"` 343 Instance_url string `xml:"Body>loginResponse>result>serverUrl"` 344 } 345 var fault SoapFault 346 if err = xml.Unmarshal(response, &fault); fault.Detail.ExceptionMessage != "" { 347 ErrorAndExit(fault.Detail.ExceptionCode + ": " + fault.Detail.ExceptionMessage) 348 } 349 if err = xml.Unmarshal(response, &result); err != nil { 350 return 351 } 352 orgid := strings.Split(result.SessionId, "!")[0] 353 u, err := url.Parse(result.Instance_url) 354 if err != nil { 355 return 356 } 357 instanceUrl := u.Scheme + "://" + u.Host 358 identity := u.Scheme + "://" + u.Host + "/id/" + orgid + "/" + result.Id 359 creds = ForceCredentials{result.SessionId, identity, instanceUrl, "", "", endpoint == EndpointCustom, "", endpoint} 360 361 return 362 } 363 364 func ForceLogin(endpoint ForceEndpoint) (creds ForceCredentials, err error) { 365 ch := make(chan ForceCredentials) 366 port, err := startLocalHttpServer(ch) 367 var url string 368 switch endpoint { 369 case EndpointProduction: 370 url = fmt.Sprintf("https://login.salesforce.com/services/oauth2/authorize?response_type=token&client_id=%s&redirect_uri=%s&state=%d&prompt=login", ProductionClientId, RedirectUri, port) 371 case EndpointTest: 372 url = fmt.Sprintf("https://test.salesforce.com/services/oauth2/authorize?response_type=token&client_id=%s&redirect_uri=%s&state=%d&prompt=login", ProductionClientId, RedirectUri, port) 373 case EndpointPrerelease: 374 url = fmt.Sprintf("https://prerellogin.pre.salesforce.com/services/oauth2/authorize?response_type=token&client_id=%s&redirect_uri=%s&state=%d&prompt=login", PrereleaseClientId, RedirectUri, port) 375 case EndpointMobile1: 376 url = fmt.Sprintf("https://EndpointMobile1.t.salesforce.com/services/oauth2/authorize?response_type=token&client_id=%s&redirect_uri=%s&state=%d&prompt=login", Mobile1ClientId, RedirectUri, port) 377 default: 378 ErrorAndExit("no such endpoint type") 379 } 380 381 err = Open(url) 382 creds = <-ch 383 creds.ForceEndpoint = endpoint 384 return 385 } 386 387 func (f *Force) GetCodeCoverage(classId string, className string) (err error) { 388 url := fmt.Sprintf("%s/services/data/%s/query/?q=Select+Id+From+ApexClass+Where+Name+=+'%s'", f.Credentials.InstanceUrl, apiVersion, className) 389 390 body, err := f.httpGet(url) 391 if err != nil { 392 return 393 } 394 var result ForceQueryResult 395 json.Unmarshal(body, &result) 396 397 classId = result.Records[0]["Id"].(string) 398 url = fmt.Sprintf("%s/services/data/%s/tooling/query/?q=Select+Coverage,+NumLinesCovered,+NumLinesUncovered,+ApexTestClassId,+ApexClassorTriggerId+From+ApexCodeCoverage+Where+ApexClassorTriggerId='%s'", f.Credentials.InstanceUrl, apiVersion, classId) 399 400 body, err = f.httpGet(url) 401 if err != nil { 402 return 403 } 404 405 //var result ForceSobjectsResult 406 json.Unmarshal(body, &result) 407 fmt.Printf("\n%d lines covered\n%d lines not covered\n", int(result.Records[0]["NumLinesCovered"].(float64)), int(result.Records[0]["NumLinesUncovered"].(float64))) 408 return 409 } 410 411 func (f *Force) GetAuraBundles() (bundles AuraDefinitionBundleResult, definitions AuraDefinitionBundleResult, err error) { 412 bundles, err = f.GetAuraBundlesList() 413 definitions, err = f.GetAuraBundleDefinitions() 414 return 415 } 416 417 func (f *Force) GetAuraBundleDefinitions() (definitions AuraDefinitionBundleResult, err error) { 418 aurl := fmt.Sprintf("%s/services/data/%s/tooling/query?q=%s", f.Credentials.InstanceUrl, apiVersion, 419 url.QueryEscape("SELECT Id, Source, AuraDefinitionBundleId, DefType, Format FROM AuraDefinition")) 420 421 body, err := f.httpGet(aurl) 422 if err != nil { 423 return 424 } 425 json.Unmarshal(body, &definitions) 426 427 return 428 } 429 430 func (f *Force) GetAuraBundlesList() (bundles AuraDefinitionBundleResult, err error) { 431 aurl := fmt.Sprintf("%s/services/data/%s/tooling/query?q=%s", f.Credentials.InstanceUrl, apiVersion, 432 url.QueryEscape("SELECT Id, DeveloperName, NamespacePrefix, ApiVersion, Description FROM AuraDefinitionBundle")) 433 body, err := f.httpGet(aurl) 434 if err != nil { 435 return 436 } 437 json.Unmarshal(body, &bundles) 438 439 return 440 } 441 442 func (f *Force) GetAuraBundle(bundleName string) (bundles AuraDefinitionBundleResult, definitions AuraDefinitionBundleResult, err error) { 443 bundles, err = f.GetAuraBundleByName(bundleName) 444 if len(bundles.Records) == 0 { 445 ErrorAndExit(fmt.Sprintf("There is no Aura bundle named %q", bundleName)) 446 } 447 bundle := bundles.Records[0] 448 definitions, err = f.GetAuraBundleDefinition(bundle["Id"].(string)) 449 return 450 } 451 452 func (f *Force) GetAuraBundleByName(bundleName string) (bundles AuraDefinitionBundleResult, err error) { 453 criteria := fmt.Sprintf(" Where DeveloperName = '%s'", bundleName) 454 455 aurl := fmt.Sprintf("%s/services/data/%s/tooling/query?q=%s", f.Credentials.InstanceUrl, apiVersion, 456 url.QueryEscape(fmt.Sprintf("SELECT Id, DeveloperName, NamespacePrefix, ApiVersion, Description FROM AuraDefinitionBundle%s", criteria))) 457 458 body, err := f.httpGet(aurl) 459 if err != nil { 460 return 461 } 462 json.Unmarshal(body, &bundles) 463 464 return 465 } 466 467 func (f *Force) GetAuraBundleDefinition(id string) (definitions AuraDefinitionBundleResult, err error) { 468 aurl := fmt.Sprintf("%s/services/data/%s/tooling/query?q=%s", f.Credentials.InstanceUrl, apiVersion, 469 url.QueryEscape("SELECT Id, Source, AuraDefinitionBundleId, DefType, Format FROM AuraDefinition")) 470 471 body, err := f.httpGet(aurl) 472 if err != nil { 473 return 474 } 475 json.Unmarshal(body, &definitions) 476 477 return 478 } 479 480 func (f *Force) CreateAuraBundle(bundleName string) (result ForceCreateRecordResult, err error) { 481 aurl := fmt.Sprintf("%s/services/data/%s/tooling/sobjects/AuraDefinitionBundle", f.Credentials.InstanceUrl, apiVersion) 482 attrs := make(map[string]string) 483 attrs["DeveloperName"] = bundleName 484 attrs["Description"] = "An Aura Bundle" 485 attrs["MasterLabel"] = bundleName 486 attrs["ApiVersion"] = apiVersionNumber 487 body, err := f.httpPost(aurl, attrs) 488 if err != nil { 489 return 490 } 491 json.Unmarshal(body, &result) 492 493 return 494 } 495 496 func (f *Force) CreateAuraComponent(attrs map[string]string) (result ForceCreateRecordResult, err error) { 497 aurl := fmt.Sprintf("%s/services/data/%s/tooling/sobjects/AuraDefinition", f.Credentials.InstanceUrl, apiVersion) 498 body, err := f.httpPost(aurl, attrs) 499 if err != nil { 500 fmt.Println("The error is: ", err.Error()) 501 return 502 } 503 json.Unmarshal(body, &result) 504 505 return 506 } 507 508 func (f *Force) ListSobjects() (sobjects []ForceSobject, err error) { 509 url := fmt.Sprintf("%s/services/data/%s/sobjects", f.Credentials.InstanceUrl, apiVersion) 510 body, err := f.httpGet(url) 511 if err != nil { 512 return 513 } 514 var result ForceSobjectsResult 515 json.Unmarshal(body, &result) 516 sobjects = result.Sobjects 517 return 518 } 519 520 func (f *Force) GetSobject(name string) (sobject ForceSobject, err error) { 521 url := fmt.Sprintf("%s/services/data/%s/sobjects/%s/describe", f.Credentials.InstanceUrl, apiVersion, name) 522 body, err := f.httpGet(url) 523 if err != nil { 524 return 525 } 526 json.Unmarshal(body, &sobject) 527 return 528 } 529 530 func (f *Force) Query(query string) (result ForceQueryResult, err error) { 531 url := fmt.Sprintf("%s/services/data/%s/query?q=%s", f.Credentials.InstanceUrl, apiVersion, url.QueryEscape(query)) 532 body, err := f.httpGet(url) 533 if err != nil { 534 return 535 } 536 json.Unmarshal(body, &result) 537 return 538 } 539 540 func (f *Force) Get(url string) (object ForceRecord, err error) { 541 body, err := f.httpGet(url) 542 if err != nil { 543 return 544 } 545 err = json.Unmarshal(body, &object) 546 return 547 } 548 549 func (f *Force) GetPasswordStatus(id string) (result ForcePasswordStatusResult, err error) { 550 url := fmt.Sprintf("%s/services/data/%s/sobjects/User/%s/password", f.Credentials.InstanceUrl, apiVersion, id) 551 body, err := f.httpGet(url) 552 if err != nil { 553 return 554 } 555 err = json.Unmarshal(body, &result) 556 return 557 } 558 559 func (f *Force) ResetPassword(id string) (result ForcePasswordResetResult, err error) { 560 url := fmt.Sprintf("%s/services/data/%s/sobjects/User/%s/password", f.Credentials.InstanceUrl, apiVersion, id) 561 body, err := f.httpDelete(url) 562 if err != nil { 563 return 564 } 565 err = json.Unmarshal(body, &result) 566 return 567 } 568 569 func (f *Force) ChangePassword(id string, attrs map[string]string) (result string, err error) { 570 url := fmt.Sprintf("%s/services/data/%s/sobjects/User/%s/password", f.Credentials.InstanceUrl, apiVersion, id) 571 _, err = f.httpPost(url, attrs) 572 return 573 } 574 575 func (f *Force) GetRecord(sobject, id string) (object ForceRecord, err error) { 576 url := fmt.Sprintf("%s/services/data/%s/sobjects/%s/%s", f.Credentials.InstanceUrl, apiVersion, sobject, id) 577 body, err := f.httpGet(url) 578 if err != nil { 579 return 580 } 581 err = json.Unmarshal(body, &object) 582 return 583 } 584 585 func (f *Force) CreateRecord(sobject string, attrs map[string]string) (id string, err error) { 586 url := fmt.Sprintf("%s/services/data/%s/sobjects/%s", f.Credentials.InstanceUrl, apiVersion, sobject) 587 body, err := f.httpPost(url, attrs) 588 var result ForceCreateRecordResult 589 json.Unmarshal(body, &result) 590 id = result.Id 591 return 592 } 593 594 func (f *Force) CreateBulkJob(xmlbody string) (result JobInfo, err error) { 595 url := fmt.Sprintf("%s/services/async/%s/job", f.Credentials.InstanceUrl, apiVersionNumber) 596 body, err := f.httpPostXML(url, xmlbody) 597 xml.Unmarshal(body, &result) 598 if len(result.Id) == 0 { 599 var fault LoginFault 600 xml.Unmarshal(body, &fault) 601 err = errors.New(fmt.Sprintf("%s: %s", fault.ExceptionCode, fault.ExceptionMessage)) 602 } 603 return 604 } 605 606 func (f *Force) CloseBulkJob(jobId string, xmlbody string) (result JobInfo, err error) { 607 url := fmt.Sprintf("%s/services/async/%s/job/%s", f.Credentials.InstanceUrl, apiVersionNumber, jobId) 608 body, err := f.httpPostXML(url, xmlbody) 609 xml.Unmarshal(body, &result) 610 if len(result.Id) == 0 { 611 var fault LoginFault 612 xml.Unmarshal(body, &fault) 613 err = errors.New(fmt.Sprintf("%s: %s", fault.ExceptionCode, fault.ExceptionMessage)) 614 } 615 return 616 } 617 618 func (f *Force) GetBulkJobs() (result []JobInfo, err error) { 619 url := fmt.Sprintf("%s/services/async/%s/jobs", f.Credentials.InstanceUrl, apiVersionNumber) 620 body, err := f.httpGetBulk(url) 621 xml.Unmarshal(body, &result) 622 if len(result[0].Id) == 0 { 623 var fault LoginFault 624 xml.Unmarshal(body, &fault) 625 err = errors.New(fmt.Sprintf("%s: %s", fault.ExceptionCode, fault.ExceptionMessage)) 626 } 627 return 628 } 629 630 func (f *Force) BulkQuery(soql string, jobId string, contettype string) (result BatchInfo, err error) { 631 url := fmt.Sprintf("%s/services/async/%s/job/%s/batch", f.Credentials.InstanceUrl, apiVersionNumber, jobId) 632 var body []byte 633 634 if contettype == "CSV" { 635 body, err = f.httpPostCSV(url, soql) 636 xml.Unmarshal(body, &result) 637 } else { 638 body, err = f.httpPostXML(url, soql) 639 xml.Unmarshal(body, &result) 640 } 641 if len(result.Id) == 0 { 642 var fault LoginFault 643 xml.Unmarshal(body, &fault) 644 err = errors.New(fmt.Sprintf("%s: %s", fault.ExceptionCode, fault.ExceptionMessage)) 645 } 646 return 647 } 648 649 func (f *Force) AddBatchToJob(xmlbody string, jobId string) (result BatchInfo, err error) { 650 url := fmt.Sprintf("%s/services/async/%s/job/%s/batch", f.Credentials.InstanceUrl, apiVersionNumber, jobId) 651 body, err := f.httpPostCSV(url, xmlbody) 652 xml.Unmarshal(body, &result) 653 if len(result.Id) == 0 { 654 var fault LoginFault 655 xml.Unmarshal(body, &fault) 656 err = errors.New(fmt.Sprintf("%s: %s", fault.ExceptionCode, fault.ExceptionMessage)) 657 } 658 return 659 } 660 661 func (f *Force) GetBatchInfo(jobId string, batchId string) (result BatchInfo, err error) { 662 url := fmt.Sprintf("%s/services/async/%s/job/%s/batch/%s", f.Credentials.InstanceUrl, apiVersionNumber, jobId, batchId) 663 body, err := f.httpGetBulk(url) 664 xml.Unmarshal(body, &result) 665 if len(result.Id) == 0 { 666 var fault LoginFault 667 xml.Unmarshal(body, &fault) 668 err = errors.New(fmt.Sprintf("%s: %s", fault.ExceptionCode, fault.ExceptionMessage)) 669 } 670 return 671 } 672 673 func (f *Force) GetBatches(jobId string) (result []BatchInfo, err error) { 674 url := fmt.Sprintf("%s/services/async/%s/job/%s/batch", f.Credentials.InstanceUrl, apiVersionNumber, jobId) 675 body, err := f.httpGetBulk(url) 676 677 var batchInfoList struct { 678 BatchInfos []BatchInfo `xml:"batchInfo"` 679 } 680 681 xml.Unmarshal(body, &batchInfoList) 682 result = batchInfoList.BatchInfos 683 if len(result) == 0 { 684 var fault LoginFault 685 xml.Unmarshal(body, &fault) 686 err = errors.New(fmt.Sprintf("%s: %s", fault.ExceptionCode, fault.ExceptionMessage)) 687 } 688 return 689 } 690 691 func (f *Force) GetJobInfo(jobId string) (result JobInfo, err error) { 692 url := fmt.Sprintf("%s/services/async/%s/job/%s", f.Credentials.InstanceUrl, apiVersionNumber, jobId) 693 body, err := f.httpGetBulk(url) 694 xml.Unmarshal(body, &result) 695 if len(result.Id) == 0 { 696 var fault LoginFault 697 xml.Unmarshal(body, &fault) 698 err = errors.New(fmt.Sprintf("%s: %s", fault.ExceptionCode, fault.ExceptionMessage)) 699 } 700 return 701 } 702 703 func (f *Force) RetrieveBulkQuery(jobId string, batchId string) (result []byte, err error) { 704 url := fmt.Sprintf("%s/services/async/%s/job/%s/batch/%s/result", f.Credentials.InstanceUrl, apiVersionNumber, jobId, batchId) 705 result, err = f.httpGetBulk(url) 706 return 707 } 708 709 func (f *Force) RetrieveBulkQueryResults(jobId string, batchId string, resultId string) (result []byte, err error) { 710 url := fmt.Sprintf("%s/services/async/%s/job/%s/batch/%s/result/%s", f.Credentials.InstanceUrl, apiVersionNumber, jobId, batchId, resultId) 711 result, err = f.httpGetBulk(url) 712 return 713 } 714 715 func (f *Force) RetrieveBulkBatchResults(jobId string, batchId string) (results BatchResult, err error) { 716 url := fmt.Sprintf("%s/services/async/%s/job/%s/batch/%s/result", f.Credentials.InstanceUrl, apiVersionNumber, jobId, batchId) 717 result, err := f.httpGetBulk(url) 718 if len(result) == 0 { 719 var fault LoginFault 720 xml.Unmarshal(result, &fault) 721 err = errors.New(fmt.Sprintf("%s: %s", fault.ExceptionCode, fault.ExceptionMessage)) 722 } 723 // sreader = Reader.NewReader(result); 724 return 725 } 726 727 func (f *Force) UpdateAuraComponent(source map[string]string, id string) (err error) { 728 url := fmt.Sprintf("%s/services/data/%s/tooling/sobjects/AuraDefinition/%s", f.Credentials.InstanceUrl, apiVersion, id) 729 _, err = f.httpPatch(url, source) 730 return 731 } 732 733 func (f *Force) DeleteToolingRecord(objecttype string, id string) (err error) { 734 url := fmt.Sprintf("%s/services/data/%s/tooling/sobjects/%s/%s", f.Credentials.InstanceUrl, apiVersion, objecttype, id) 735 _, err = f.httpDelete(url) 736 return 737 } 738 739 func (f *Force) UpdateRecord(sobject string, id string, attrs map[string]string) (err error) { 740 url := fmt.Sprintf("%s/services/data/%s/sobjects/%s/%s", f.Credentials.InstanceUrl, apiVersion, sobject, id) 741 _, err = f.httpPatch(url, attrs) 742 return 743 } 744 745 func (f *Force) DeleteRecord(sobject string, id string) (err error) { 746 url := fmt.Sprintf("%s/services/data/%s/sobjects/%s/%s", f.Credentials.InstanceUrl, apiVersion, sobject, id) 747 _, err = f.httpDelete(url) 748 return 749 } 750 751 func (f *Force) Whoami() (me ForceRecord, err error) { 752 parts := strings.Split(f.Credentials.Id, "/") 753 me, err = f.GetRecord("User", parts[len(parts)-1]) 754 return 755 } 756 757 func (f *Force) httpGet(url string) (body []byte, err error) { 758 body, err = f.httpGetRequest(url, "Authorization", fmt.Sprintf("Bearer %s", f.Credentials.AccessToken)) 759 return 760 } 761 762 func (f *Force) httpGetBulk(url string) (body []byte, err error) { 763 body, err = f.httpGetRequest(url, "X-SFDC-Session", fmt.Sprintf("Bearer %s", f.Credentials.AccessToken)) 764 return 765 } 766 767 func (f *Force) httpGetRequest(url string, headerName string, headerValue string) (body []byte, err error) { 768 req, err := httpRequest("GET", url, nil) 769 if err != nil { 770 return 771 } 772 req.Header.Add(headerName, headerValue) 773 res, err := httpClient().Do(req) 774 if err != nil { 775 return 776 } 777 defer res.Body.Close() 778 if res.StatusCode == 401 { 779 err = errors.New("authorization expired, please run `force login`") 780 return 781 } 782 if res.StatusCode == 403 { 783 err = errors.New("Forbidden; Your authorization may have expired, or you do not have access. Please run `force login` and try again") 784 return 785 } 786 body, err = ioutil.ReadAll(res.Body) 787 if res.StatusCode/100 != 2 { 788 var messages []ForceError 789 json.Unmarshal(body, &messages) 790 if len(messages) > 0 { 791 err = errors.New(messages[0].Message) 792 } else { 793 err = errors.New(string(body)) 794 } 795 return 796 } 797 return 798 } 799 800 func (f *Force) httpPostCSV(url string, data string) (body []byte, err error) { 801 body, err = f.httpPostWithContentType(url, data, "text/csv") 802 return 803 } 804 805 func (f *Force) httpPostXML(url string, data string) (body []byte, err error) { 806 body, err = f.httpPostWithContentType(url, data, "application/xml") 807 return 808 } 809 810 func (f *Force) httpPostWithContentType(url string, data string, contenttype string) (body []byte, err error) { 811 rbody := data 812 req, err := httpRequest("POST", url, bytes.NewReader([]byte(rbody))) 813 if err != nil { 814 return 815 } 816 817 req.Header.Add("X-SFDC-Session", f.Credentials.AccessToken) 818 req.Header.Add("Content-Type", contenttype) 819 res, err := httpClient().Do(req) 820 if err != nil { 821 return 822 } 823 defer res.Body.Close() 824 if res.StatusCode == 401 { 825 err = errors.New("authorization expired, please run `force login`") 826 return 827 } 828 body, err = ioutil.ReadAll(res.Body) 829 830 if res.StatusCode/100 != 2 { 831 var messages []ForceError 832 json.Unmarshal(body, &messages) 833 if messages != nil { 834 err = errors.New(messages[0].Message) 835 } 836 return 837 } 838 return 839 } 840 841 /*func (f *Force) httpGet(url string) (body []byte, err error) { 842 req, err := httpRequest("GET", url, nil) 843 if err != nil { 844 return 845 } 846 req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", f.Credentials.AccessToken)) 847 res, err := httpClient().Do(req) 848 if err != nil { 849 return 850 } 851 defer res.Body.Close() 852 if res.StatusCode == 401 { 853 err = errors.New("authorization expired, please run `force login`") 854 return 855 } 856 if res.StatusCode == 403 { 857 err = errors.New("Forbidden; Your authorization may have expired, or you do not have access. Please run `force login` and try again") 858 return 859 } 860 body, err = ioutil.ReadAll(res.Body) 861 if res.StatusCode/100 != 2 { 862 var messages []ForceError 863 json.Unmarshal(body, &messages) 864 if len(messages) > 0 { 865 err = errors.New(messages[0].Message) 866 } else { 867 err = errors.New(string(body)) 868 } 869 return 870 } 871 return 872 }*/ 873 874 func (f *Force) httpPost(url string, attrs map[string]string) (body []byte, err error) { 875 rbody, _ := json.Marshal(attrs) 876 req, err := httpRequest("POST", url, bytes.NewReader(rbody)) 877 if err != nil { 878 return 879 } 880 req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", f.Credentials.AccessToken)) 881 req.Header.Add("Content-Type", "application/json") 882 res, err := httpClient().Do(req) 883 if err != nil { 884 return 885 } 886 defer res.Body.Close() 887 if res.StatusCode == 401 { 888 err = errors.New("authorization expired, please run `force login`") 889 return 890 } 891 body, err = ioutil.ReadAll(res.Body) 892 if res.StatusCode/100 != 2 { 893 var messages []ForceError 894 json.Unmarshal(body, &messages) 895 err = errors.New(messages[0].Message) 896 return 897 } 898 return 899 } 900 901 func (f *Force) httpPatch(url string, attrs map[string]string) (body []byte, err error) { 902 rbody, _ := json.Marshal(attrs) 903 req, err := httpRequest("PATCH", url, bytes.NewReader(rbody)) 904 if err != nil { 905 return 906 } 907 req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", f.Credentials.AccessToken)) 908 req.Header.Add("Content-Type", "application/json") 909 res, err := httpClient().Do(req) 910 if err != nil { 911 return 912 } 913 defer res.Body.Close() 914 if res.StatusCode == 401 { 915 err = errors.New("Authorization expired, please run `force login`") 916 return 917 } 918 body, err = ioutil.ReadAll(res.Body) 919 if res.StatusCode/100 != 2 { 920 var messages []ForceError 921 json.Unmarshal(body, &messages) 922 err = errors.New(messages[0].Message) 923 return 924 } 925 return 926 } 927 928 func (f *Force) httpDelete(url string) (body []byte, err error) { 929 req, err := httpRequest("DELETE", url, nil) 930 if err != nil { 931 return 932 } 933 req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", f.Credentials.AccessToken)) 934 res, err := httpClient().Do(req) 935 if err != nil { 936 return 937 } 938 defer res.Body.Close() 939 if res.StatusCode == 401 { 940 err = errors.New("authorization expired, please run `force login`") 941 return 942 } 943 body, err = ioutil.ReadAll(res.Body) 944 if res.StatusCode/100 != 2 { 945 var messages []ForceError 946 json.Unmarshal(body, &messages) 947 err = errors.New(messages[0].Message) 948 return 949 } 950 return 951 } 952 953 func httpClient() (client *http.Client) { 954 if CustomEndpoint == "" { 955 chain := rootCertificate() 956 config := tls.Config{InsecureSkipVerify: true} 957 config.RootCAs = x509.NewCertPool() 958 for _, cert := range chain.Certificate { 959 x509Cert, err := x509.ParseCertificate(cert) 960 if err != nil { 961 panic(err) 962 } 963 config.RootCAs.AddCert(x509Cert) 964 } 965 config.BuildNameToCertificate() 966 tr := http.Transport{TLSClientConfig: &config} 967 client = &http.Client{Transport: &tr} 968 } else { 969 client = &http.Client{} 970 } 971 return 972 } 973 974 func httpRequest(method, url string, body io.Reader) (request *http.Request, err error) { 975 request, err = http.NewRequest(method, url, body) 976 if err != nil { 977 return 978 } 979 request.Header.Add("User-Agent", fmt.Sprintf("force/%s (%s-%s)", Version, runtime.GOOS, runtime.GOARCH)) 980 return 981 } 982 983 func rootCertificate() (cert tls.Certificate) { 984 certPEMBlock := []byte(RootCertificates) 985 var certDERBlock *pem.Block 986 for { 987 certDERBlock, certPEMBlock = pem.Decode(certPEMBlock) 988 if certDERBlock == nil { 989 break 990 } 991 if certDERBlock.Type == "CERTIFICATE" { 992 cert.Certificate = append(cert.Certificate, certDERBlock.Bytes) 993 } 994 } 995 return 996 } 997 998 func startLocalHttpServer(ch chan ForceCredentials) (port int, err error) { 999 listener, err := net.Listen("tcp", ":0") 1000 if err != nil { 1001 return 1002 } 1003 port = listener.Addr().(*net.TCPAddr).Port 1004 h := http.NewServeMux() 1005 h.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 1006 w.Header().Set("Access-Control-Allow-Origin", "https://force-cli.herokuapp.com") 1007 if r.Method == "OPTIONS" { 1008 w.Header().Set("Access-Control-Allow-Headers", "X-Requested-With") 1009 } else { 1010 query := r.URL.Query() 1011 var creds ForceCredentials 1012 creds.AccessToken = query.Get("access_token") 1013 creds.Id = query.Get("id") 1014 creds.InstanceUrl = query.Get("instance_url") 1015 creds.IssuedAt = query.Get("issued_at") 1016 creds.Scope = query.Get("scope") 1017 ch <- creds 1018 if _, ok := r.Header["X-Requested-With"]; ok == false { 1019 http.Redirect(w, r, "https://force-cli.herokuapp.com/auth/complete", http.StatusSeeOther) 1020 } 1021 listener.Close() 1022 } 1023 }) 1024 go http.Serve(listener, h) 1025 return 1026 }