diff --git a/authenticators/DigestAuthenticator.cls b/authenticators/DigestAuthenticator.cls index cf1c3bda..783287a2 100644 --- a/authenticators/DigestAuthenticator.cls +++ b/authenticators/DigestAuthenticator.cls @@ -95,6 +95,7 @@ End Sub Private Sub IAuthenticator_AfterExecute(ByVal Client As RestClient, ByVal Request As RestRequest, ByRef Response As RestResponse) If Response.StatusCode = 401 And Not Me.IsAuthenticated Then + RestHelpers.LogDebug "Extract Authenticate and retry 401 request " & Request.FullUrl(Client.BaseUrl), "Digest.AfterExecute" ExtractAuthenticateInformation Response Request.AddHeader "Authorization", CreateHeader(Client, Request) @@ -115,6 +116,7 @@ End Sub Private Sub IAuthenticator_HttpOpen(ByRef Http As Object, ByVal Client As RestClient, ByRef Request As RestRequest, BaseUrl As String, Optional UseAsync As Boolean = False) ' Perform standard http open Call Http.Open(Request.MethodName(), Request.FullUrl(BaseUrl), UseAsync) + RestHelpers.LogDebug Request.MethodName() & Request.FullUrl(BaseUrl), "DigestAuthenticator.HttpOpen" End Sub Public Function CreateHeader(Client As RestClient, Request As RestRequest) As String @@ -131,6 +133,8 @@ Public Function CreateHeader(Client As RestClient, Request As RestRequest) As St ", cnonce=""" & Me.ClientNonce & """" & _ ", response=""" & CalculateResponse(Client, Request) & """" & _ ", opaque=""" & Me.Opaque & """" + + RestHelpers.LogDebug CreateHeader, "DigestAuthenticator.CreateHeader" End Function Public Function CalculateResponse(Client As RestClient, Request As RestRequest) As String @@ -143,6 +147,7 @@ Public Function CalculateResponse(Client As RestClient, Request As RestRequest) HA2 = CalculateHA2(Request.MethodName, Uri) CalculateResponse = RestHelpers.MD5(HA1 & ":" & Me.ServerNonce & ":" & FormattedRequestCount & ":" & Me.ClientNonce & ":" & qop & ":" & HA2) + RestHelpers.LogDebug CalculateResponse, "DigestAuthenticator.CalculateResponse" End Function ' Extract authentication information from 401 response headers @@ -172,6 +177,8 @@ Public Sub ExtractAuthenticateInformation(Response As RestResponse) If Key = "nonce" Then Me.ServerNonce = Value If Key = "opaque" Then Me.Opaque = Value Next i + + RestHelpers.LogDebug "realm=" & Me.Realm & ", nonce=" & Me.ServerNonce & ", opaque=" & Me.Opaque, "DigestAuthenticator.ExtractAuthenticateInformation" End If Exit Sub @@ -181,10 +188,12 @@ End Sub Public Function CalculateHA1() As String CalculateHA1 = MD5(Me.Username & ":" & Me.Realm & ":" & Me.Password) + RestHelpers.LogDebug CalculateHA1 & " for " & Me.Username & ":" & Me.Realm & ":" & RestHelpers.Obfuscate(Me.Password), "DigestAuthenticator.CalculateHA1" End Function Public Function CalculateHA2(Method As String, Uri As String) As String CalculateHA2 = MD5(Method & ":" & Uri) + RestHelpers.LogDebug CalculateHA2 & " for " & Method & ":" & Uri, "DigestAuthenticator.CalculateHA2" End Function ' Pad request count to 8 places diff --git a/authenticators/EmptyAuthenticator.cls b/authenticators/EmptyAuthenticator.cls index 0ab4686b..92f4115b 100644 --- a/authenticators/EmptyAuthenticator.cls +++ b/authenticators/EmptyAuthenticator.cls @@ -73,4 +73,5 @@ Private Sub IAuthenticator_HttpOpen(ByRef Http As Object, ByVal Client As RestCl ' Perform standard http open Call Http.Open(Request.MethodName(), Request.FullUrl(BaseUrl), UseAsync) + RestHelpers.LogDebug Request.MethodName() & Request.FullUrl(BaseUrl), "Authenticator.HttpOpen" End Sub diff --git a/authenticators/FacebookAuthenticator.cls b/authenticators/FacebookAuthenticator.cls index 51c7552d..1f1573fb 100644 --- a/authenticators/FacebookAuthenticator.cls +++ b/authenticators/FacebookAuthenticator.cls @@ -55,7 +55,9 @@ Public Property Get Token() As String EndIndex = InStr(StartIndex, Response.Content, "&expires=") pToken = Mid$(Response.Content, StartIndex, EndIndex - StartIndex) + RestHelpers.LogDebug "Received token: " & RestHelpers.Obfuscate(pToken), "FacebookAuthenticator.Token" Else + RestHelpers.LogError "Failed to load token: " & Response.StatusCode & " - " & Response.Content, "FacebookAuthenticator.Token" Err.Raise vbObjectError + Response.StatusCode, _ Description:="Failed to load Bearer Token: " & Response.StatusCode & " - " & Response.Content End If @@ -95,6 +97,7 @@ Public Property Get LoginUrl() ScopeString = Me.Scope End If LoginUrl = LoginUrl & "&scope=" & ScopeString + RestHelpers.LogDebug LoginUrl, "FacebookAuthenticator.LoginUrl" End Property ' ============================================= ' @@ -140,6 +143,7 @@ Public Sub Login() Code = OAuthExtractCode(IE) Else ' Login failed + RestHelpers.LogError "Facebook login failed or was denied", "FacebookAuthenticator.Login" Err.Raise vbObjectError + 1, "OAuthDialog", "Login failed or was denied" End If End With @@ -150,13 +154,16 @@ CleanUp: Set IE = Nothing If Not Completed Then + RestHelpers.LogError "Facebook login did not complete", "FacebookAuthenticator.Login" Err.Raise vbObjectError + 1, "OAuthDialog", "Login did not complete" ElseIf Err.Number <> 0 Then ' Rethrow error Err.Raise Err.Number, Err.Source, Err.Description ElseIf Left(Code, 5) = "Error" Then + RestHelpers.LogError "Facebook login returned error: " & Code, "FacebookAuthenticator.Login" Err.Raise vbObjectError + 1, "OAuthDialog", Code Else + RestHelpers.LogDebug "Successfully logged in: " & Code, "FacebookAuthenticator.Login" ' Success! Me.Code = Code @@ -187,6 +194,7 @@ End Sub Private Sub IAuthenticator_BeforeExecute(ByVal Client As RestClient, ByRef Request As RestRequest) Request.AddQuerystringParam "access_token", Me.Token + RestHelpers.LogDebug "Add access_token=" & Me.Token, "FacebookAuthenticator.BeforeExecute" End Sub '' @@ -214,6 +222,7 @@ End Sub Private Sub IAuthenticator_HttpOpen(ByRef Http As Object, ByVal Client As RestClient, ByRef Request As RestRequest, BaseUrl As String, Optional UseAsync As Boolean = False) ' Perform standard http open Call Http.Open(Request.MethodName(), Request.FullUrl(BaseUrl), UseAsync) + RestHelpers.LogDebug Request.MethodName() & Request.FullUrl(BaseUrl), "FacebookAuthenticator.HttpOpen" End Sub Private Function TokenRequest() As RestRequest @@ -267,6 +276,7 @@ Private Function OAuthExtractCode(IE As Object) As String If StartIndex >= 0 And EndIndex > StartIndex Then OAuthExtractCode = Mid$(Url, StartIndex, EndIndex - StartIndex) Else + RestHelpers.LogError "Unrecognized token format: " & Url, "FacebookAuthenticator.OAuthExtractCode" OAuthExtractCode = "Error: Unrecognized token formatting" End If End Function diff --git a/authenticators/GoogleAuthenticator.cls b/authenticators/GoogleAuthenticator.cls index 5717f95f..7d1d0b11 100644 --- a/authenticators/GoogleAuthenticator.cls +++ b/authenticators/GoogleAuthenticator.cls @@ -110,6 +110,7 @@ Public Property Get LoginUrl() As String Request.AddQuerystringParam "scope", ScopeString LoginUrl = Request.FormattedResource + RestHelpers.LogDebug LoginUrl, "GoogleAuthenticator.LoginUrl" Set Request = Nothing End Property @@ -129,7 +130,9 @@ Public Property Get Token() As String Dim Data As Object Set Data = RestHelpers.ParseJSON(Response.Content) pToken = Data("access_token") + RestHelpers.LogDebug "Received Token: " & RestHelpers.Obfuscate(pToken), "GoogleAuthenticator.Token" Else + RestHelpers.LogError "Failed to load token: " & Response.StatusCode & " - " & Response.Content, "GoogleAuthenticator.Token" Err.Raise vbObjectError + Response.StatusCode, _ Description:="Failed to load Bearer Token: " & Response.StatusCode & " - " & Response.Content End If @@ -197,14 +200,17 @@ Public Sub Login(Optional APIKey As String = "") Completed = True If OAuthIsDenied(IE) Then ' Login failed + RestHelpers.LogError "Login failed or was denied", "GoogleAuthenticator.Login" Err.Raise vbObjectError + 1, "OAuthDialog", "Login failed or was denied" Else Code = OAuthExtractCode(IE) If Left(Code, 5) = "Error" Then + RestHelpers.LogError "Login error: " & Code, "GoogleAuthenticator.Login" Err.Raise vbObjectError + 1, "OAuthDialog", Code Else ' Success! Me.AuthorizationCode = Code + RestHelpers.LogDebug "Login success: " & Code, "GoogleAuthenticator.Login" ' Temporarily assign token to force request Dim Token As String @@ -220,6 +226,7 @@ CleanUp: Set IE = Nothing If Not Completed Then + RestHelpers.LogError "Login did not complete", "GoogleAuthenticator.Login" Err.Raise vbObjectError + 1, "OAuthDialog", "Login did not complete" ElseIf Err.Number <> 0 Then ' Rethrow error @@ -305,8 +312,10 @@ End Sub Private Sub IAuthenticator_BeforeExecute(ByVal Client As RestClient, ByRef Request As RestRequest) If Me.APIKey <> "" Then Request.AddQuerystringParam "key", Me.APIKey + RestHelpers.LogDebug "Login with key: " & RestHelpers.Obfuscate(Me.APIKey), "GoogleAuthenticator.BeforeExecute" Else Request.AddHeader "Authorization", "Bearer " & Me.Token + RestHelpers.LogDebug "Login with token: " & RestHelpers.Obfuscate(Me.Token), "GoogleAuthenticator.BeforeExecute" End If End Sub @@ -335,6 +344,7 @@ End Sub Private Sub IAuthenticator_HttpOpen(ByRef Http As Object, ByVal Client As RestClient, ByRef Request As RestRequest, BaseUrl As String, Optional UseAsync As Boolean = False) ' Perform standard http open Http.Open Request.MethodName(), Request.FullUrl(BaseUrl), UseAsync + RestHelpers.LogDebug Request.MethodName() & Request.FullUrl(BaseUrl), "FacebookAuthenticator.HttpOpen" End Sub Private Function TokenRequest() As RestRequest diff --git a/authenticators/HttpBasicAuthenticator.cls b/authenticators/HttpBasicAuthenticator.cls index 9122440a..a654838d 100644 --- a/authenticators/HttpBasicAuthenticator.cls +++ b/authenticators/HttpBasicAuthenticator.cls @@ -86,6 +86,7 @@ Private Sub IAuthenticator_HttpOpen(ByRef Http As Object, ByVal Client As RestCl ' Use http open with username and password values set ' (This is used in addition to setting request header, as some services required this) Http.Open Request.MethodName(), Request.FullUrl(BaseUrl), UseAsync, Me.Username, Me.Password + RestHelpers.LogDebug Request.MethodName() & Request.FullUrl(BaseUrl), "FacebookAuthenticator.HttpOpen" End Sub Private Function CreateHeader() As String diff --git a/authenticators/OAuth1Authenticator.cls b/authenticators/OAuth1Authenticator.cls index fe306539..bc33e980 100644 --- a/authenticators/OAuth1Authenticator.cls +++ b/authenticators/OAuth1Authenticator.cls @@ -136,6 +136,7 @@ End Sub Private Sub IAuthenticator_HttpOpen(ByRef Http As Object, ByVal Client As RestClient, ByRef Request As RestRequest, BaseUrl As String, Optional UseAsync As Boolean = False) ' Perform standard http open Call Http.Open(Request.MethodName(), Request.FullUrl(BaseUrl), UseAsync) + RestHelpers.LogDebug Request.MethodName() & Request.FullUrl(BaseUrl), "FacebookAuthenticator.HttpOpen" End Sub Public Function CreateHeader(Client As RestClient, Request As RestRequest) As String @@ -180,6 +181,8 @@ Public Function CreateHeader(Client As RestClient, Request As RestRequest) As St Header = Header & "oauth_token=" & Chr(34) & Me.Token & Chr(34) & ", " Header = Header & "oauth_version=" & Chr(34) & "1.0" & Chr(34) CreateHeader = Header + + RestHelpers.LogDebug CreateHeader, "OAuth1Authenticator.CreateHeader" End Function Public Function CreateBaseString(Nonce As String, Timestamp As String, Client As RestClient, Request As RestRequest) As String @@ -200,6 +203,7 @@ Public Function CreateBaseString(Nonce As String, Timestamp As String, Client As base = base & "&" & "oauth_version=1.0" CreateBaseString = Request.MethodName() & "&" & RestHelpers.UrlEncode(RequestUrl(Client, Request)) & "&" & RestHelpers.UrlEncode(base) + RestHelpers.LogDebug CreateBaseString, "OAuth1Authenticator.CreateBaseString" End Function Public Function RequestUrl(Client As RestClient, Request As RestRequest) As String @@ -239,7 +243,11 @@ Public Function RequestParameters(Client As RestClient, Request As RestRequest) Dim Parts As Dictionary Set Parts = RestHelpers.UrlParts(Request.FullUrl(Client.BaseUrl)) - RequestParameters = RestHelpers.UrlDecode(Replace(Parts("Querystring"), "?", "")) + ' Remove leading ? + RequestParameters = Replace(Parts("Querystring"), "?", "") + + ' Replace + for spaces with %20 + RequestParameters = Replace(RequestParameters, "+", "%20") End Function Public Function CreateSigningKey() As String @@ -248,6 +256,7 @@ End Function Public Function CreateSignature(base As String, signingKey As String) As String CreateSignature = RestHelpers.Base64_HMACSHA1(base, signingKey) + RestHelpers.LogDebug CreateSignature, "OAuth1Authenticator.CreateSignature" End Function Public Function CreateTimestamp() As String diff --git a/authenticators/OAuth2Authenticator.cls b/authenticators/OAuth2Authenticator.cls index 2103a11d..bd48ed4c 100644 --- a/authenticators/OAuth2Authenticator.cls +++ b/authenticators/OAuth2Authenticator.cls @@ -90,6 +90,7 @@ Private Sub IAuthenticator_BeforeExecute(ByVal Client As RestClient, ByRef Reque If Http.Status <> 200 Then ' Error getting OAuth2 token + RestHelpers.LogError "Token request failed: " & Http.Status & " - " & Http.ResponseText, "OAuth2Authenticator.BeforeExecute" Err.Raise vbObjectError + Http.Status, _ Description:="Failed to retrieve OAuth2 Token - " & Http.Status & ": " & Http.ResponseText End If @@ -99,6 +100,7 @@ Private Sub IAuthenticator_BeforeExecute(ByVal Client As RestClient, ByRef Reque If Not Response Is Nothing Then If Response.Exists(Me.TokenKey) Then Me.Token = Response(Me.TokenKey) + RestHelpers.LogDebug "Received token: " & RestHelpers.Obfuscate(Me.Token), "OAuth2Authenticator.BeforeExecute" End If ' (Salesforce specific, but shouldn't affect any other OAuth2 clients) @@ -106,6 +108,7 @@ Private Sub IAuthenticator_BeforeExecute(ByVal Client As RestClient, ByRef Reque Request.BaseUrl = Response("instance_url") End If Else + RestHelpers.LogError "Failed to read OAuth2 Token: " & Http.ResponseText, "OAuth2Authenticator.BeforeExecute" Err.Raise vbObjectError + 2, _ Description:="Failed to read OAuth2 Token" End If @@ -148,6 +151,7 @@ End Sub Private Sub IAuthenticator_HttpOpen(ByRef Http As Object, ByVal Client As RestClient, ByRef Request As RestRequest, BaseUrl As String, Optional UseAsync As Boolean = False) ' Perform standard http open Call Http.Open(Request.MethodName(), Request.FullUrl(BaseUrl), UseAsync) + RestHelpers.LogDebug Request.MethodName() & Request.FullUrl(BaseUrl), "FacebookAuthenticator.HttpOpen" End Sub Private Function CreateHeader() As String @@ -165,4 +169,5 @@ Private Function CreateTokenRequest() As String CreateTokenRequest = CreateTokenRequest & "&client_secret=" & Me.ClientSecret CreateTokenRequest = CreateTokenRequest & "&username=" & Me.Username CreateTokenRequest = CreateTokenRequest & "&password=" & Me.Password + RestHelpers.LogDebug CreateTokenRequest, "OAuth2Authenticator.CreateTokenRequest" End Function diff --git a/authenticators/TwitterAuthenticator.cls b/authenticators/TwitterAuthenticator.cls index 8fc0a6e0..84684b9a 100644 --- a/authenticators/TwitterAuthenticator.cls +++ b/authenticators/TwitterAuthenticator.cls @@ -48,7 +48,9 @@ Public Property Get Token() As String ' Store token if successful, otherwise throw error If Response.StatusCode = Ok Then pToken = Response.Data("access_token") + RestHelpers.LogDebug "Successfully received token: " & RestHelpers.Obfuscate(pToken), "TwitterAuthenticator.Token" Else + RestHelpers.LogError "Failed to load token: " & Response.StatusCode & " - " & Response.Content, "TwitterAuthenticator.Token" Err.Raise vbObjectError + Response.StatusCode, _ Description:="Failed to load Bearer Token: " & Response.StatusCode & " - " & Response.Content End If @@ -121,6 +123,7 @@ Private Sub IAuthenticator_HttpOpen(ByRef Http As Object, ByVal Client As RestCl ' Perform standard http open Call Http.Open(Request.MethodName(), Request.FullUrl(BaseUrl), UseAsync) + RestHelpers.LogDebug "Http.Open " & Request.MethodName() & Request.FullUrl(BaseUrl), "FacebookAuthenticator.HttpOpen" End Sub Private Function TokenRequest() As RestRequest diff --git a/specs/Excel-REST - Specs.xlsm b/specs/Excel-REST - Specs.xlsm index 28b23c66..140f428d 100644 Binary files a/specs/Excel-REST - Specs.xlsm and b/specs/Excel-REST - Specs.xlsm differ diff --git a/specs/OAuth1AuthenticatorSpecs.bas b/specs/OAuth1AuthenticatorSpecs.bas index b503cfde..6c570f43 100644 --- a/specs/OAuth1AuthenticatorSpecs.bas +++ b/specs/OAuth1AuthenticatorSpecs.bas @@ -61,7 +61,17 @@ Public Function Specs() As SpecSuite Request.AddParameter "c", "Howdy!" Request.AddQuerystringParam "d", 789 - .Expect(Auth.RequestParameters(Client, Request)).ToEqual "a=123&b=456&c=Howdy!&d=789" + .Expect(Auth.RequestParameters(Client, Request)).ToEqual "a=123&b=456&c=Howdy%21&d=789" + End With + + With Specs.It("should handle spaces in parameters correctly") + Client.BaseUrl = "http://localhost:3000/" + Set Request = New RestRequest + Request.Resource = "testing" + Request.AddQuerystringParam "a", "a b" + + .Expect(Auth.RequestParameters(Client, Request)).ToEqual "a=a%20b" + .Expect(Request.FullUrl(Client.BaseUrl)).ToEqual "http://localhost:3000/testing?a=a+b" End With Set Client = New RestClient @@ -95,3 +105,67 @@ Public Function Specs() As SpecSuite InlineRunner.RunSuite Specs End Function + +' LinkedIn Specific +' ----------------- ' +Sub LinkedInSpecs() + Dim Specs As New SpecSuite + + Dim Client As New RestClient + Client.BaseUrl = "http://api.linkedin.com/v1/" + + Dim Auth As New OAuth1Authenticator + Dim ConsumerKey As String + Dim ConsumerSecret As String + Dim Token As String + Dim TokenSecret As String + + If Credentials.Loaded Then + ConsumerKey = Credentials.Values("LinkedIn")("api_key") + ConsumerSecret = Credentials.Values("LinkedIn")("api_secret") + Token = Credentials.Values("LinkedIn")("user_token") + TokenSecret = Credentials.Values("LinkedIn")("user_secret") + Else + ConsumerKey = InputBox("Enter Consumer Key") + ConsumerSecret = InputBox("Enter Consumer Secret") + Token = InputBox("Enter Token") + TokenSecret = InputBox("Enter Token Secret") + End If + Auth.Setup _ + ConsumerKey:=ConsumerKey, _ + ConsumerSecret:=ConsumerSecret, _ + Token:=Token, _ + TokenSecret:=TokenSecret + + Set Client.Authenticator = Auth + + Dim Request As RestRequest + Dim Response As RestResponse + + With Specs.It("should get profile") + Set Request = New RestRequest + Request.Resource = "people/~?format={format}" + + Set Response = Client.Execute(Request) + + .Expect(Response.StatusCode).ToEqual 200 + .Expect(Response.Data("firstName")).ToBeDefined + End With + + With Specs.It("should search with space") + Set Request = New RestRequest + Request.Resource = "company-search?format={format}" + Request.AddQuerystringParam "keywords", "microsoft corp" + + Set Response = Client.Execute(Request) + + .Expect(Response.StatusCode).ToEqual 200 + .Expect(Response.Data("companies")).ToBeDefined + + If (Response.StatusCode <> 200) Then + Debug.Print "Error :" & Response.StatusCode & " - " & Response.Content + End If + End With + + InlineRunner.RunSuite Specs +End Sub diff --git a/src/RestClient.cls b/src/RestClient.cls index a9fef6a0..b0c2375d 100644 --- a/src/RestClient.cls +++ b/src/RestClient.cls @@ -49,6 +49,7 @@ Public Function Execute(Request As RestRequest) As RestResponse On Error GoTo ErrorHandling Dim Http As Object + RestHelpers.LogDebug Request.FullUrl(Me.BaseUrl), "RestClient.Execute" Set Http = HttpSetup(Request, False) Set Execute = RestHelpers.ExecuteRequest(Http, Request) @@ -79,6 +80,7 @@ Public Function ExecuteAsync(Request As RestRequest, Callback As String, Optiona Dim Http As Object ' Setup the request + RestHelpers.LogDebug Request.FullUrl(Me.BaseUrl), "RestClient.ExecuteAsync" Set Http = HttpSetup(Request, True) RestHelpers.ExecuteRequestAsync Http, Request, Me.TimeoutMS, Callback, CallbackArgs ExecuteAsync = True @@ -110,7 +112,8 @@ Public Function GetJSON(Url As String, Optional Options As Dictionary) As RestRe Request.Format = json Request.Method = httpGET - Set GetJSON = Me.Execute(Request) + RestHelpers.LogDebug Request.FullUrl(Me.BaseUrl), "RestClient.GetJSON" + Set GetJSON = Me.Execute(Request) End Function '' @@ -134,6 +137,7 @@ Public Function PostJSON(Url As String, Body As Dictionary, Optional Options As Request.Method = httpPOST Request.AddBody Body + RestHelpers.LogDebug Request.FullUrl(Me.BaseUrl), "RestClient.PostJSON" Set PostJSON = Me.Execute(Request) End Function @@ -175,6 +179,7 @@ Private Function HttpSetup(ByRef Request As RestRequest, Optional UseAsync As Bo Else ' Nothing hooked in so open http object HttpSetup.Open Request.MethodName(), Request.FullUrl(Me.BaseUrl), UseAsync + RestHelpers.LogDebug "Http.Open " & Request.MethodName() & Request.FullUrl(Me.BaseUrl), "RestClient.HttpSetup" End If RestHelpers.SetHeaders HttpSetup, Request diff --git a/src/RestHelpers.bas b/src/RestHelpers.bas index c4c38563..6997caa9 100644 --- a/src/RestHelpers.bas +++ b/src/RestHelpers.bas @@ -67,10 +67,42 @@ Public Enum StatusCodes GatewayTimeout = 504 End Enum +Public EnableLogging As Boolean + ' ============================================= ' ' Shared Helpers ' ============================================= ' +'' +' Log helper +' +' @param {String} Message +' @param {String} [From] +' --------------------------------------------- ' + +Public Sub LogDebug(Message As String, Optional From As String = "") + If EnableLogging Then + If From = "" Then + From = "Excel-REST" + End If + + Debug.Print From & ": " & Message + End If +End Sub +Public Sub LogError(Message As String, Optional From As String = "", Optional ErrNumber As Long = -1) + If From = "" Then + From = "Excel-REST" + End If + If ErrNumber >= 0 Then + From = From & ": " & ErrNumber + End If + + Debug.Print "ERROR - " & From & ": " & ErrNumber & " " & ErrDescription +End Sub +Public Function Obfuscate(Secure As String, Optional Character As String = "*") As String + Obfuscate = String(Len(Secure), Character) +End Function + '' ' Parse given JSON string into object (Dictionary or Collection) ' @@ -435,6 +467,7 @@ Public Function PrepareHttpRequest(Request As RestRequest, TimeoutMS As Long, _ If Request.IncludeContentLength Then Request.AddHeader "Content-Length", Request.ContentLength + LogDebug "Content-Length: " & Request.ContentLength, "RestHelpers.PrepareHttpRequest" Else If Request.Headers.Exists("Content-Length") Then Request.Headers.Remove "Content-Length" @@ -461,11 +494,13 @@ Public Sub SetHeaders(ByRef Http As Object, Request As RestRequest) Dim HeaderKey As Variant For Each HeaderKey In Request.Headers.Keys() Http.setRequestHeader HeaderKey, Request.Headers(HeaderKey) + LogDebug HeaderKey & ": " & Request.Headers(HeaderKey), "RestHelpers.SetHeaders" Next HeaderKey Dim CookieKey As Variant For Each CookieKey In Request.Cookies.Keys() Http.setRequestHeader "Cookie", CookieKey & "=" & Request.Cookies(CookieKey) + LogDebug "Cookie: " & CookieKey & "=" & Request.Cookies(CookieKey), "RestHelpers.SetHeaders" Next CookieKey End Sub @@ -483,9 +518,11 @@ Public Sub PrepareProxyForHttpRequest(ByRef Http As Object, ProxyServer As Strin If ProxyServer <> "" Then Http.SetProxy 2, ProxyServer, BypassList + LogDebug "SetProxy: " & ProxyServer & ", " & BypassList, "RestHelpers.PrepareProxyForHttpRequest" If Username <> "" Then Http.SetProxyCredentials Username, Password + LogDebug "SetProxyCredentials: " & Username & ", " & Obfuscate(Password), "RestHelpers.PrepareProxyForHttpRequest" End If End If End Sub @@ -504,6 +541,7 @@ Public Function ExecuteRequest(ByRef Http As Object, ByRef Request As RestReques ' Send the request and handle response Http.Send Request.Body + LogDebug "Http.Send: " & Request.Body, "RestHelpers.ExecuteRequest" Set Response = RestHelpers.CreateResponseFromHttp(Http, Request.Format) ErrorHandling: @@ -513,9 +551,11 @@ ErrorHandling: If InStr(Err.Description, "The operation timed out") > 0 Then ' Return 408 Set Response = RestHelpers.CreateResponse(StatusCodes.RequestTimeout, "Request Timeout") + LogDebug "Timeout: " & Request.FullUrl, "RestHelpers.ExecuteRequest" Err.Clear Else ' Rethrow error + LogError Err.Description, "RestHelpers.ExecuteRequest", Err.Number Err.Raise Err.Number, Description:=Err.Description End If End If @@ -541,6 +581,7 @@ Public Sub ExecuteRequestAsync(ByRef Http As Object, ByRef Request As RestReques ' Send the request Request.StartTimeoutTimer TimeoutMS Http.Send Request.Body + LogDebug "Http.Send: " & Request.Body, "RestHelpers.ExecuteRequestAsync" Exit Sub @@ -548,6 +589,7 @@ ErrorHandling: ' Close http and rethrow error If Not Http Is Nothing Then Set Http = Nothing + LogError Err.Description, "RestHelpers.ExecuteRequestAsync", Err.Number Err.Raise Err.Number, Description:=Err.Description End Sub @@ -566,6 +608,8 @@ Public Function CreateResponseFromHttp(ByRef Http As Object, Optional Format As CreateResponseFromHttp.Body = Http.ResponseBody CreateResponseFromHttp.Content = Http.ResponseText + LogDebug "CreateResponse: " & Http.Status & ", " & Left(Http.ResponseText, 100), "RestHelpers.CreateResponseFromHttp" + ' Convert content to data by format Select Case Format Case AvailableFormats.formurlencoded @@ -658,8 +702,10 @@ Public Function ExtractHeadersFromResponseHeaders(ResponseHeaders As String) As ' Close out multi-line string Multiline = False Headers.Add Header + LogDebug Header("key") & "=" & Header("value"), "RestHelpers.ExtractHeadersFromResponseHeaders" ElseIf Not Header Is Nothing Then Headers.Add Header + LogDebug Header("key") & "=" & Header("value"), "RestHelpers.ExtractHeadersFromResponseHeaders" End If If Not Multiline Then @@ -771,6 +817,7 @@ Sub TimeoutTimerExpired(ByVal HWnd As Long, ByVal uMsg As Long, _ #End If StopTimeoutTimer Request + LogDebug "Async Timeout: " & Request.FullUrl, "RestHelpers.TimeoutTimerExpired" Request.TimedOut End Sub diff --git a/src/RestRequest.cls b/src/RestRequest.cls index 34518210..2bd48622 100644 --- a/src/RestRequest.cls +++ b/src/RestRequest.cls @@ -139,6 +139,8 @@ Public Property Get FormattedResource() As String End If End If End If + + RestHelpers.LogDebug Me.Resource & " => " & FormattedResource, "RestRequest.FormattedResource" End Property Public Property Get Body() As String @@ -169,6 +171,8 @@ Public Property Get Body() As String End Select End If End If + + RestHelpers.LogDebug Body, "RestRequest.Body" End Property Public Property Get FullUrl(Optional ClientBaseUrl As String = "") As String @@ -195,6 +199,7 @@ Public Property Get FullUrl(Optional ClientBaseUrl As String = "") As String End If FullUrl = JoinUrl(Me.BaseUrl, Formatted) + RestHelpers.LogDebug Me.BaseUrl & " + " & Formatted & " => " & FullUrl, "RestRequest.FullUrl" End Property Public Property Get MethodName() As String @@ -375,6 +380,7 @@ Attribute ReadyStateChangeHandler.VB_UserMemId = 0 ' Once loaded, process result If Me.HttpRequest.readyState = 4 Then Me.StopTimeoutTimer + RestHelpers.LogDebug "ReadyState = 4", "RestRequest.ReadyStateChangeHandler" ' Callback RunCallback RestHelpers.CreateResponseFromHttp(Me.HttpRequest, Me.Format) @@ -406,6 +412,7 @@ End Sub Public Sub TimedOut() ' Callback + RestHelpers.LogDebug "Timed out", "RestRequest.TimedOut" RunCallback RestHelpers.CreateResponse(StatusCodes.RequestTimeout, "Request Timeout") End Sub @@ -440,6 +447,7 @@ Private Sub RunCallback(Response As RestResponse) Auth.AfterExecute Client, Me, Response End If If Me.Callback <> "" Then + RestHelpers.LogDebug Me.Callback, "RestRequest.RunCallback" If Not IsMissing(Me.CallbackArgs) Then Application.Run Me.Callback, Response, Me.CallbackArgs Else