From c93dfe5ab58fcafc6891c3415694677f0c959c2a Mon Sep 17 00:00:00 2001 From: Blake Bender Date: Fri, 14 Aug 2015 15:06:09 -0700 Subject: [PATCH 01/11] Add property set for unknown field names of JSON objects. Work in progress, builds but haven't run it yet. --- .../FBGraphObjectHeader.ttinclude | 5 +++ .../FBGraphObjectImplementation.ttinclude | 34 +++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FBGraphObjectHeader.ttinclude b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FBGraphObjectHeader.ttinclude index fcd2313..f511e5c 100644 --- a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FBGraphObjectHeader.ttinclude +++ b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FBGraphObjectHeader.ttinclude @@ -89,6 +89,10 @@ namespace Facebook Platform::String^ JsonText ); + property Windows::Foundation::Collections::PropertySet^ Fields + { + Windows::Foundation::Collections::PropertySet^ get(); + } <# foreach (XmlNode child in rootNode.SelectNodes("property")) @@ -129,6 +133,7 @@ namespace Facebook <# } #> + Windows::Foundation::Collections::PropertySet^ _fields; }; } } diff --git a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FBGraphObjectImplementation.ttinclude b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FBGraphObjectImplementation.ttinclude index 5ba2193..cb6fd06 100644 --- a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FBGraphObjectImplementation.ttinclude +++ b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FBGraphObjectImplementation.ttinclude @@ -86,9 +86,13 @@ using namespace Windows::UI::Xaml::Data; } #> { - ; + _fields = ref new PropertySet(); } + PropertySet^ <#= className #>::Fields::get() + { + return _fields; + } <# for (int i = 0; i < props.Count; i++) { @@ -177,7 +181,33 @@ Object^ <#= className #>::FromJson( } <# } -#> +#> + else + { + String^ value = nullptr; + + switch (it->Current->Value->ValueType) + { + case JsonValueType::Boolean: + value = it->Current->Value->GetBoolean().ToString(); + break; + case JsonValueType::Number: + value = it->Current->Value->GetNumber().ToString(); + break; + case JsonValueType::String: + value = it->Current->Value->GetString(); + break; + case JsonValueType::Array: + case JsonValueType::Object: + value = it->Current->Value->Stringify(); + break; + } + + if (value) + { + result->_fields->Insert(key, value); + } + } } if (!found) From 75d1ac7a031ed299079c7321ca137574031387ba Mon Sep 17 00:00:00 2001 From: Austin Diviness Date: Mon, 20 Jun 2016 10:16:09 -0700 Subject: [PATCH 02/11] wrote test for json property set --- .../FBWinStoreCsTests.csproj | 4 ++- .../TestData/testJsonPropertySet.json | 11 +++++++ winsdkfb/FBWinStoreCsTests/UnitTest1.cs | 32 +++++++++++++++++++ winsdkfb/FBWinStoreCsTests/data.json | 0 .../FBWinStoreCsTests.csproj | 4 +++ 5 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 winsdkfb/FBWinStoreCsTests/TestData/testJsonPropertySet.json delete mode 100644 winsdkfb/FBWinStoreCsTests/data.json diff --git a/winsdkfb/FBWinStoreCsTests/FBWinStoreCsTests.csproj b/winsdkfb/FBWinStoreCsTests/FBWinStoreCsTests.csproj index 03f9662..55d401d 100644 --- a/winsdkfb/FBWinStoreCsTests/FBWinStoreCsTests.csproj +++ b/winsdkfb/FBWinStoreCsTests/FBWinStoreCsTests.csproj @@ -114,13 +114,15 @@ winsdkfb_testing_key.pfx - + + PreserveNewest + diff --git a/winsdkfb/FBWinStoreCsTests/TestData/testJsonPropertySet.json b/winsdkfb/FBWinStoreCsTests/TestData/testJsonPropertySet.json new file mode 100644 index 0000000..5afff38 --- /dev/null +++ b/winsdkfb/FBWinStoreCsTests/TestData/testJsonPropertySet.json @@ -0,0 +1,11 @@ +{ + "name": "test_user", + "test_boolean": false, + "test_num": 4, + "test_string": "hello_world", + "test_array": [ 2, 3, 5, 7, 11 ], + "test_object": { + "a": "b", + "c": "d" + } +} \ No newline at end of file diff --git a/winsdkfb/FBWinStoreCsTests/UnitTest1.cs b/winsdkfb/FBWinStoreCsTests/UnitTest1.cs index 76c43eb..bca8d71 100644 --- a/winsdkfb/FBWinStoreCsTests/UnitTest1.cs +++ b/winsdkfb/FBWinStoreCsTests/UnitTest1.cs @@ -15,6 +15,7 @@ //****************************************************************************** using System; +using System.IO; using System.Collections.Generic; using System.Linq; using System.Text; @@ -700,5 +701,36 @@ namespace FBWinStoreCsTests Assert.IsFalse(likes.HasPrevious); // test with next but no previous } + + [TestMethod] + public async Task testJsonPropertySet() + { + + StorageFolder installedLocation = Windows.ApplicationModel.Package.Current.InstalledLocation; + StorageFolder jsonFolder = await installedLocation.GetFolderAsync("TestData"); + StorageFile jsonTestFile = await jsonFolder.GetFileAsync("testJsonPropertySet.json"); + string text = await FileIO.ReadTextAsync(jsonTestFile); + + FBUser testUser = (FBUser)FBUser.FromJson(text); + // member variable + Assert.AreEqual(testUser.Name, "test_user"); + Assert.IsFalse(testUser.Fields.ContainsKey("name")); + // boolean field + Assert.IsTrue(testUser.Fields.ContainsKey("test_boolean")); + Assert.AreEqual(testUser.Fields["test_boolean"], "false"); + // num field + Assert.IsTrue(testUser.Fields.ContainsKey("test_num")); + Assert.AreEqual(testUser.Fields["test_num"], "4"); + // string field + Assert.IsTrue(testUser.Fields.ContainsKey("test_string")); + Assert.AreEqual(testUser.Fields["test_string"], "hello_world"); + // array field + Assert.IsTrue(testUser.Fields.ContainsKey("test_array")); + Assert.AreEqual(testUser.Fields["test_array"], "[2,3,5,7,11]"); + // object field + Assert.IsTrue(testUser.Fields.ContainsKey("test_object")); + Assert.AreEqual(testUser.Fields["test_object"], @"{""a"":""b"",""c"":""d""}"); + + } } } diff --git a/winsdkfb/FBWinStoreCsTests/data.json b/winsdkfb/FBWinStoreCsTests/data.json deleted file mode 100644 index e69de29..0000000 diff --git a/winsdkfb/winsdkfb_uwp/FBWinStoreCsTests/FBWinStoreCsTests.csproj b/winsdkfb/winsdkfb_uwp/FBWinStoreCsTests/FBWinStoreCsTests.csproj index 14aef32..ac8e0ce 100644 --- a/winsdkfb/winsdkfb_uwp/FBWinStoreCsTests/FBWinStoreCsTests.csproj +++ b/winsdkfb/winsdkfb_uwp/FBWinStoreCsTests/FBWinStoreCsTests.csproj @@ -98,6 +98,10 @@ FBCSObjectImplementation.ttinclude + + TestData\testJsonPropertySet.json + PreserveNewest + Utility.ttinclude From 5ff22dfbff0c6beaff230253b6dd3e62b2746eaf Mon Sep 17 00:00:00 2001 From: Austin Diviness Date: Tue, 28 Jun 2016 12:32:31 -0700 Subject: [PATCH 03/11] FBClient::SerializeParameters uses ToString() as a backup --- samples/LoginCpp-UWP/LoginCpp/LoginCpp.vcxproj | 6 +++--- samples/LoginCpp-UWP/LoginCpp/LoginCpp.vcxproj.filters | 2 -- winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.cpp | 8 ++++++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/samples/LoginCpp-UWP/LoginCpp/LoginCpp.vcxproj b/samples/LoginCpp-UWP/LoginCpp/LoginCpp.vcxproj index 3bef6db..4573f19 100644 --- a/samples/LoginCpp-UWP/LoginCpp/LoginCpp.vcxproj +++ b/samples/LoginCpp-UWP/LoginCpp/LoginCpp.vcxproj @@ -210,14 +210,14 @@ + + + {973a943b-ff77-4267-8f30-f5fe2b7f5583} - - - \ No newline at end of file diff --git a/samples/LoginCpp-UWP/LoginCpp/LoginCpp.vcxproj.filters b/samples/LoginCpp-UWP/LoginCpp/LoginCpp.vcxproj.filters index a5583a1..8e1d616 100644 --- a/samples/LoginCpp-UWP/LoginCpp/LoginCpp.vcxproj.filters +++ b/samples/LoginCpp-UWP/LoginCpp/LoginCpp.vcxproj.filters @@ -34,7 +34,6 @@ - @@ -47,7 +46,6 @@ - diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.cpp b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.cpp index d3d44e9..58cb1be 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.cpp +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.cpp @@ -654,8 +654,12 @@ void FBClient::SerializeParameters( auto item = keysThatAreNotString->First(); while (item->HasCurrent) { - // TODO: Jsonize the object value - String^ newValue = dynamic_cast(parameters->Lookup(item->Current)); + Object^ val = parameters->Lookup(item->Current); + String^ newValue = dynamic_cast(val); + if (!newValue) + { + newValue = val->ToString(); + } // Replace the existing object with the new Jsonized value parameters->Remove(item->Current); From dc48c5e857255aa6a5e3daf7a4367ffc0004c33f Mon Sep 17 00:00:00 2001 From: Austin Diviness Date: Thu, 30 Jun 2016 14:17:28 -0700 Subject: [PATCH 04/11] fixed bug when all json values go into the property set --- .../FBWinStoreCsTests/FBWinStoreCsTests.csproj | 3 +++ .../testJsonPropertySetNoBuiltInFields.json | 10 ++++++++++ winsdkfb/FBWinStoreCsTests/UnitTest1.cs | 13 +++++++++++++ .../FBGraphObjectImplementation.ttinclude | 15 ++++++++------- 4 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 winsdkfb/FBWinStoreCsTests/TestData/testJsonPropertySetNoBuiltInFields.json diff --git a/winsdkfb/FBWinStoreCsTests/FBWinStoreCsTests.csproj b/winsdkfb/FBWinStoreCsTests/FBWinStoreCsTests.csproj index 55d401d..b4c120a 100644 --- a/winsdkfb/FBWinStoreCsTests/FBWinStoreCsTests.csproj +++ b/winsdkfb/FBWinStoreCsTests/FBWinStoreCsTests.csproj @@ -123,6 +123,9 @@ PreserveNewest + + PreserveNewest + diff --git a/winsdkfb/FBWinStoreCsTests/TestData/testJsonPropertySetNoBuiltInFields.json b/winsdkfb/FBWinStoreCsTests/TestData/testJsonPropertySetNoBuiltInFields.json new file mode 100644 index 0000000..8eaaefa --- /dev/null +++ b/winsdkfb/FBWinStoreCsTests/TestData/testJsonPropertySetNoBuiltInFields.json @@ -0,0 +1,10 @@ +{ + "test_boolean": false, + "test_num": 4, + "test_string": "hello_world", + "test_array": [ 2, 3, 5, 7, 11 ], + "test_object": { + "a": "b", + "c": "d" + } +} diff --git a/winsdkfb/FBWinStoreCsTests/UnitTest1.cs b/winsdkfb/FBWinStoreCsTests/UnitTest1.cs index bca8d71..cd7f75a 100644 --- a/winsdkfb/FBWinStoreCsTests/UnitTest1.cs +++ b/winsdkfb/FBWinStoreCsTests/UnitTest1.cs @@ -732,5 +732,18 @@ namespace FBWinStoreCsTests Assert.AreEqual(testUser.Fields["test_object"], @"{""a"":""b"",""c"":""d""}"); } + + [TestMethod] + public async Task testJsonPropertySetNoBuiltInFields() + { + StorageFolder installedLocation = Windows.ApplicationModel.Package.Current.InstalledLocation; + StorageFolder jsonFolder = await installedLocation.GetFolderAsync("TestData"); + StorageFile jsonTestFile = await jsonFolder.GetFileAsync("testJsonPropertySetNoBuiltInFields.json"); + string text = await FileIO.ReadTextAsync(jsonTestFile); + + FBUser testUser = (FBUser)FBUser.FromJson(text); + Assert.AreNotEqual(testUser, null); + } + } } diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/FBGraphObjectImplementation.ttinclude b/winsdkfb/winsdkfb/winsdkfb.Shared/FBGraphObjectImplementation.ttinclude index cceeeae..75cb8e9 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/FBGraphObjectImplementation.ttinclude +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/FBGraphObjectImplementation.ttinclude @@ -38,7 +38,7 @@ using namespace winsdkfb::Graph; using namespace Platform; using namespace Windows::Data::Json; using namespace Windows::Foundation::Collections; -<# +<# if (bindable) { #> @@ -122,7 +122,7 @@ void <#= className #>::<#= propName #>::set(<#= rtType #> value) } #> Object^ <#= className #>::FromJson( - String^ JsonText + String^ JsonText ) { <#= className #>^ result = ref new <#= className #>; @@ -181,7 +181,7 @@ Object^ <#= className #>::FromJson( } <# } -#> +#> else { String^ value = nullptr; @@ -200,19 +200,20 @@ Object^ <#= className #>::FromJson( case JsonValueType::Array: case JsonValueType::Object: value = it->Current->Value->Stringify(); - break; + break; } if (value) { result->_fields->Insert(key, value); + found++; } } } if (!found) { - // No field names matched any known properties for this class. + // No field names matched any known properties for this class. // Even if it *is* an object of our type, it's not useful. result = nullptr; } @@ -220,7 +221,7 @@ Object^ <#= className #>::FromJson( } return result; } -<# +<# if (bindable) { #> @@ -230,7 +231,7 @@ void <#= className #>::NotifyPropertyChanged( ) { CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( - Windows::UI::Core::CoreDispatcherPriority::Normal, + Windows::UI::Core::CoreDispatcherPriority::Normal, ref new Windows::UI::Core::DispatchedHandler([this, prop]() { PropertyChangedEventArgs^ args = ref new PropertyChangedEventArgs(prop); From e5359ef060b9eb505c0b4e27da1747aea441cd1e Mon Sep 17 00:00:00 2001 From: Austin Diviness Date: Wed, 6 Jul 2016 14:57:00 -0700 Subject: [PATCH 05/11] updated default graph api version to 2.6 --- winsdkfb/winsdkfb/winsdkfb.Shared/FacebookSession.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookSession.cpp b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookSession.cpp index 0b533f4..c2fb041 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookSession.cpp +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookSession.cpp @@ -99,7 +99,7 @@ FBSession::FBSession() : login_evt = CreateEventEx(NULL, NULL, 0, DELETE | SYNCHRONIZE); } _APIMajorVersion = 2; - _APIMinorVersion = 1; + _APIMinorVersion = 6; #if WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP _webViewRedirectDomain = FACEBOOK_MOBILE_SERVER_NAME; #else @@ -205,9 +205,12 @@ task FBSession::GetUserInfo( winsdkfb::FBAccessTokenData^ TokenData ) { + PropertySet^ parameters = ref new PropertySet(); + parameters->Insert(L"fields", + L"gender,link,first_name,last_name,locale,timezone,email,updated_time,verified,name,id"); FBSingleValue^ value = ref new FBSingleValue( "/me", - nullptr, + parameters, ref new FBJsonClassFactory([](String^ JsonText) -> Object^ { return FBUser::FromJson(JsonText); From 1c11ab407ce396053eef40b12dde2dd39b1f30fd Mon Sep 17 00:00:00 2001 From: Austin Diviness Date: Fri, 23 Sep 2016 16:18:16 -0700 Subject: [PATCH 06/11] FBPaginatedArray pages are formatted correctly for being used with FBClient --- .../winsdkfb.Shared/FacebookClient.cpp | 25 +++++++++++ .../winsdkfb/winsdkfb.Shared/FacebookClient.h | 5 +++ .../FacebookPaginatedArray.cpp | 41 +++++++++++++++++-- .../winsdkfb.Shared/FacebookPaginatedArray.h | 8 +++- 4 files changed, 75 insertions(+), 4 deletions(-) diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.cpp b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.cpp index f255244..9917771 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.cpp +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.cpp @@ -403,6 +403,13 @@ Uri^ FBClient::PrepareRequestUri( dataWriter->UnicodeEncoding = UnicodeEncoding::Utf8; dataWriter->ByteOrder = ByteOrder::LittleEndian; + if (parameters == nullptr) + { + parameters = ref new PropertySet(); + } + // remove any query string parameters from path and put them in parameters + path = MovePathQueryStringToParameters(path, parameters); + PropertySet^ mediaObjects = ref new PropertySet(); PropertySet^ mediaStreams = ref new PropertySet(); PropertySet^ parametersWithoutMediaObjects = ToDictionary(parameters, mediaObjects, mediaStreams); @@ -749,3 +756,21 @@ task FBClient::TryReceiveHttpResponse( return result; }); } + +String^ FBClient::MovePathQueryStringToParameters(String^ path, PropertySet^ parameters) +{ + Uri^ uri = ref new Uri(L"http://blah.com/" + path); + WwwFormUrlDecoder^ query = uri->QueryParsed; + if (query->Size > 0) + { + IIterator^ it = query->First(); + while (it->HasCurrent) + { + IWwwFormUrlDecoderEntry^ current = it->Current; + parameters->Insert(current->Name, current->Value); + it->MoveNext(); + } + } + return uri->Path; + +} diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.h b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.h index 301c549..a8bce7c 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.h +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.h @@ -228,6 +228,11 @@ namespace winsdkfb Windows::Foundation::Collections::PropertySet^ Streams ); + Platform::String^ FBClient::MovePathQueryStringToParameters( + Platform::String^ path, + Windows::Foundation::Collections::PropertySet^ parameters + ); + concurrency::task TryReceiveHttpResponse( concurrency::task httpRequestTask, concurrency::cancellation_token_source cancellationTokenSource diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.cpp b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.cpp index 69df888..2773511 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.cpp +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.cpp @@ -21,6 +21,8 @@ #include "FacebookSession.h" #include "HttpManager.h" +#include + using namespace concurrency; using namespace winsdkfb; using namespace winsdkfb::Graph; @@ -128,7 +130,8 @@ IVectorView^ FBPaginatedArray::ObjectArrayFromJsonArray( } FBResult^ FBPaginatedArray::ConsumePagedResponse( - String^ JsonText + String^ JsonText, + String^ OriginalPath ) { FBResult^ result = nullptr; @@ -160,6 +163,7 @@ FBResult^ FBPaginatedArray::ConsumePagedResponse( if (_paging) { foundPaging = true; + FormatPagingPaths(_paging, OriginalPath); } } else if (!String::CompareOrdinal(it->Current->Key, L"data")) @@ -231,7 +235,7 @@ Windows::Foundation::IAsyncOperation^ FBPaginatedArray::GetPage( return create_async([this, path]() -> task { return create_task(HttpManager::Instance->GetTaskAsync(path, _parameters)) - .then([this](String^ responseString) + .then([this, path](String^ responseString) { if (responseString == nullptr) { @@ -239,8 +243,39 @@ Windows::Foundation::IAsyncOperation^ FBPaginatedArray::GetPage( } else { - return ConsumePagedResponse(responseString); + return ConsumePagedResponse(responseString, path); } }); }); } + +void FBPaginatedArray::FormatPagingPaths( + FBPaging^ paging, + String^ path +) +{ + String^ regexString = L"(?:.*?)(" + path + L".*)"; + std::wstring stdRegexString = std::wstring{ regexString->Data() }; + std::wregex relativePathRegex{ stdRegexString }; + std::wsmatch match; + if (paging->Next) + { + std::wstring nextUrl = std::wstring{ paging->Next->Data() }; + std::regex_match(nextUrl, match, relativePathRegex); + if (match.size() >= 2) + { + std::wstring matchText = match[1].str(); + paging->Next = ref new String(matchText.c_str()); + } + } + if (paging->Previous) + { + std::wstring previousUrl = std::wstring{ paging->Previous->Data() }; + std::regex_match(previousUrl, match, relativePathRegex); + if (match.size() >= 2) + { + std::wstring matchText = match[1].str(); + paging->Next = ref new String(matchText.c_str()); + } + } +} \ No newline at end of file diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.h b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.h index b83dad2..be4d9a7 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.h +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.h @@ -143,13 +143,19 @@ namespace winsdkfb ); FBResult^ ConsumePagedResponse( - Platform::String^ JsonText + Platform::String^ JsonText, + Platform::String^ OriginalPath ); Windows::Foundation::IAsyncOperation^ GetPage( Platform::String^ path ); + void FormatPagingPaths( + FBPaging^ paging, + Platform::String^ path + ); + Windows::Foundation::Collections::IVectorView^ _current; FBPaging^ _paging; Platform::String^ _request; From b65f3dcd3a27e652ac058bcbc4c01ee6625b64d5 Mon Sep 17 00:00:00 2001 From: Johan Lindfors Date: Tue, 4 Oct 2016 14:40:40 +0200 Subject: [PATCH 07/11] Don't instantiate the FacebookDialog too early, especially if you are trying a silent login when starting the app, from an arbitrary thread --- winsdkfb/winsdkfb/winsdkfb.Shared/FacebookSession.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookSession.cpp b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookSession.cpp index c2fb041..0391915 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookSession.cpp +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookSession.cpp @@ -964,7 +964,6 @@ IAsyncOperation^ FBSession::LoginAsync( { Permissions = ref new FBPermissions((ref new Vector())->GetView()); } - _dialog = ref new FacebookDialog(); return create_async([=]() { From a0df253b45603d9d1757a8f1487f85fc9260e64f Mon Sep 17 00:00:00 2001 From: Johan Lindfors Date: Tue, 4 Oct 2016 14:41:28 +0200 Subject: [PATCH 08/11] Make sure to try and save the updated token data, like when having explicitly set an extended token --- winsdkfb/winsdkfb/winsdkfb.Shared/FacebookSession.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookSession.cpp b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookSession.cpp index 0391915..4b24031 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookSession.cpp +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookSession.cpp @@ -160,6 +160,9 @@ FBAccessTokenData^ FBSession::AccessTokenData::get() void FBSession::AccessTokenData::set(FBAccessTokenData^ value) { _AccessTokenData = value; + + // If token have been updated, make sure to save updated token + TrySaveTokenData(); } FBUser^ FBSession::User::get() From 39e2e95dcef8a93d04dd2c2d4759d22b334569a2 Mon Sep 17 00:00:00 2001 From: Austin Diviness Date: Tue, 4 Oct 2016 15:22:00 -0700 Subject: [PATCH 09/11] Fixes for FBPaginatedArray - Made IHttpClient methods use a read only data structure for parameters - FBClient parameters will not be overwritten by query string values in the request path --- winsdkfb/FBWinStoreCsTests/MockHttpClient.cs | 8 ++-- .../winsdkfb.Shared/FBSingleValue.cpp | 6 +-- .../winsdkfb.Shared/FacebookClient.cpp | 46 +++++++++++++------ .../winsdkfb/winsdkfb.Shared/FacebookClient.h | 12 +++-- .../winsdkfb.Shared/FacebookDialog.xaml.cpp | 6 +-- .../FacebookPaginatedArray.cpp | 24 +++++----- .../winsdkfb.Shared/FacebookPaginatedArray.h | 6 +-- .../winsdkfb/winsdkfb.Shared/HttpManager.cpp | 8 ++-- .../winsdkfb/winsdkfb.Shared/HttpManager.h | 8 ++-- .../winsdkfb/winsdkfb.Shared/IHttpClient.h | 8 ++-- .../FBAppEvents/FacebookAppEvents.cpp | 4 +- 11 files changed, 79 insertions(+), 57 deletions(-) diff --git a/winsdkfb/FBWinStoreCsTests/MockHttpClient.cs b/winsdkfb/FBWinStoreCsTests/MockHttpClient.cs index 2f6a68c..8abebf7 100644 --- a/winsdkfb/FBWinStoreCsTests/MockHttpClient.cs +++ b/winsdkfb/FBWinStoreCsTests/MockHttpClient.cs @@ -29,7 +29,7 @@ namespace FBWinStoreCsTests { public String ResponseData; - public IAsyncOperation DeleteTaskAsync(string path, PropertySet parameters) + public IAsyncOperation DeleteTaskAsync(string path, IReadOnlyDictionary parameters) { return Task.Run(() => { @@ -37,7 +37,7 @@ namespace FBWinStoreCsTests }).AsAsyncOperation(); } - public IAsyncOperation GetTaskAsync(string path, PropertySet parameters) + public IAsyncOperation GetTaskAsync(string path, IReadOnlyDictionary parameters) { return Task.Run(() => { @@ -45,12 +45,12 @@ namespace FBWinStoreCsTests }).AsAsyncOperation(); } - public string ParametersToQueryString(PropertySet Parameters) + public string ParametersToQueryString(IReadOnlyDictionary Parameters) { throw new NotImplementedException(); } - public IAsyncOperation PostTaskAsync(string path, PropertySet parameters) + public IAsyncOperation PostTaskAsync(string path, IReadOnlyDictionary parameters) { return Task.Run(() => { diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/FBSingleValue.cpp b/winsdkfb/winsdkfb/winsdkfb.Shared/FBSingleValue.cpp index db58bf8..df3e0fb 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/FBSingleValue.cpp +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/FBSingleValue.cpp @@ -146,13 +146,13 @@ Windows::Foundation::IAsyncOperation^ FBSingleValue::MakeHttpRequest( switch (httpMethod) { case HttpMethod::Get: - innerTask = create_task(HttpManager::Instance->GetTaskAsync(_request, _parameters)); + innerTask = create_task(HttpManager::Instance->GetTaskAsync(_request, _parameters->GetView())); break; case HttpMethod::Post: - innerTask = create_task(HttpManager::Instance->PostTaskAsync(_request, _parameters)); + innerTask = create_task(HttpManager::Instance->PostTaskAsync(_request, _parameters->GetView())); break; case HttpMethod::Delete: - innerTask = create_task(HttpManager::Instance->DeleteTaskAsync(_request, _parameters)); + innerTask = create_task(HttpManager::Instance->DeleteTaskAsync(_request, _parameters->GetView())); break; default: OutputDebugString(L"FBSingleValue::MakeHttpRequest recieved unknown HttpMethod value\n"); diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.cpp b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.cpp index 9917771..f213b9b 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.cpp +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.cpp @@ -96,13 +96,14 @@ PropertySet^ FBClient::ToDictionary(PropertySet^ parameters, PropertySet^ mediaO IAsyncOperation^ FBClient::GetTaskAsync( String^ path, - PropertySet^ parameters + IMapView^ parameters ) { + PropertySet^ modifiableParams = MapViewToPropertySet(parameters); IAsyncOperation^ myTask = create_async([=]() { Uri^ uri = FBClient::PrepareRequestUri(HttpMethod::Get, path, - parameters, nullptr); + modifiableParams, nullptr); return FBClient::GetTaskInternalAsync(uri) .then([=](String^ Response) @@ -318,32 +319,33 @@ task FBClient::MultipartPostInternalAsync( IAsyncOperation^ FBClient::PostTaskAsync( String^ path, - PropertySet^ parameters + IMapView^ parameters ) { + PropertySet^ modifiableParams = MapViewToPropertySet(parameters); IAsyncOperation^ result = nullptr; - PropertySet^ streams = GetStreamsToUpload(parameters); + PropertySet^ streams = GetStreamsToUpload(modifiableParams); if (streams) { - result = FBClient::MultipartPostAsync(path, parameters, streams); + result = FBClient::MultipartPostAsync(path, modifiableParams, streams); } else { - result = FBClient::SimplePostAsync(path, parameters); + result = FBClient::SimplePostAsync(path, modifiableParams); } - return result; } Windows::Foundation::IAsyncOperation^ FBClient::DeleteTaskAsync( String^ path, - PropertySet^ parameters + IMapView^ parameters ) { + PropertySet^ modifiableParams = MapViewToPropertySet(parameters); return create_async([=]() { Uri^ uri = FBClient::PrepareRequestUri(HttpMethod::Delete, path, - parameters, nullptr); + modifiableParams, nullptr); return FBClient::DeleteTaskInternalAsync(uri) .then([=](String^ Response) @@ -602,7 +604,7 @@ Uri^ FBClient::PrepareRequestUri( queryString += L"method=delete&"; } #endif - queryString += ParametersToQueryString(parametersWithoutMediaObjects); + queryString += ParametersToQueryString(parametersWithoutMediaObjects->GetView()); } String^ host; @@ -674,7 +676,7 @@ void FBClient::SerializeParameters( } String^ FBClient::ParametersToQueryString( - PropertySet^ Parameters + IMapView^ parameters ) { String^ queryString = L""; @@ -683,7 +685,7 @@ String^ FBClient::ParametersToQueryString( // do not need to be uploaded as multipart, i.e. any which is are not // binary data, are required to be in the query string, even for POST // requests! - IIterator^>^ kvp = Parameters->First(); + IIterator^>^ kvp = parameters->First(); while (kvp->HasCurrent) { String^ key = Uri::EscapeComponent(kvp->Current->Key); @@ -767,10 +769,26 @@ String^ FBClient::MovePathQueryStringToParameters(String^ path, PropertySet^ par while (it->HasCurrent) { IWwwFormUrlDecoderEntry^ current = it->Current; - parameters->Insert(current->Name, current->Value); + // we don't want to overwrite an explicitly specified value from the user + if (!parameters->HasKey(current->Name)) + { + parameters->Insert(current->Name, current->Value); + } it->MoveNext(); } } return uri->Path; - } + +PropertySet^ FBClient::MapViewToPropertySet(IMapView^ mapView) +{ + PropertySet^ propertySet = ref new PropertySet(); + IIterator^>^ it = mapView->First(); + while (it->HasCurrent) + { + IKeyValuePair^ current = it->Current; + propertySet->Insert(current->Key, current->Value); + it->MoveNext(); + } + return propertySet; +} \ No newline at end of file diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.h b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.h index a8bce7c..e3698b3 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.h +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.h @@ -42,7 +42,7 @@ namespace winsdkfb */ GetTaskAsync( Platform::String^ path, - Windows::Foundation::Collections::PropertySet^ parameters + Windows::Foundation::Collections::IMapView^ parameters ); /** @@ -56,7 +56,7 @@ namespace winsdkfb virtual Windows::Foundation::IAsyncOperation^ PostTaskAsync( Platform::String^ path, - Windows::Foundation::Collections::PropertySet^ parameters + Windows::Foundation::Collections::IMapView^ parameters ); /** @@ -70,7 +70,7 @@ namespace winsdkfb virtual Windows::Foundation::IAsyncOperation^ DeleteTaskAsync( Platform::String^ path, - Windows::Foundation::Collections::PropertySet^ parameters + Windows::Foundation::Collections::IMapView^ parameters ); /** @@ -80,7 +80,7 @@ namespace winsdkfb * "key1=value1&key2=value2&..." etc. */ virtual Platform::String^ ParametersToQueryString( - Windows::Foundation::Collections::PropertySet^ Parameters + Windows::Foundation::Collections::IMapView^ parameters ); private: @@ -237,5 +237,9 @@ namespace winsdkfb concurrency::task httpRequestTask, concurrency::cancellation_token_source cancellationTokenSource ); + + Windows::Foundation::Collections::PropertySet^ MapViewToPropertySet( + Windows::Foundation::Collections::IMapView^ mapView + ); }; }; diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookDialog.xaml.cpp b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookDialog.xaml.cpp index 0286757..381af95 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookDialog.xaml.cpp +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookDialog.xaml.cpp @@ -347,7 +347,7 @@ Uri^ FacebookDialog::BuildFeedDialogUrl( L"&redirect_uri=" + GetRedirectUriString(L"feed") + L"&display=popup" + L"&app_id=" + sess->FBAppId; - String^ queryString = HttpManager::Instance->ParametersToQueryString(Parameters); + String^ queryString = HttpManager::Instance->ParametersToQueryString(Parameters->GetView()); if (queryString->Length() > 0) { dialogUriString += "&" + queryString; @@ -372,7 +372,7 @@ Uri^ FacebookDialog::BuildRequestsDialogUrl( L"&redirect_uri=" + GetRedirectUriString(L"requests") + L"&display=popup" + L"&app_id=" + sess->FBAppId; - String^ queryString = HttpManager::Instance->ParametersToQueryString(Parameters); + String^ queryString = HttpManager::Instance->ParametersToQueryString(Parameters->GetView()); if (queryString->Length() > 0) { dialogUriString += "&" + queryString; @@ -397,7 +397,7 @@ Uri^ FacebookDialog::BuildSendDialogUrl( L"&redirect_uri=" + GetRedirectUriString(L"send") + L"&display=popup" + L"&app_id=" + sess->FBAppId; - String^ queryString = HttpManager::Instance->ParametersToQueryString(Parameters); + String^ queryString = HttpManager::Instance->ParametersToQueryString(Parameters->GetView()); if (queryString->Length() > 0) { dialogUriString += "&" + queryString; diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.cpp b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.cpp index 2773511..74a986e 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.cpp +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.cpp @@ -44,6 +44,10 @@ FBPaginatedArray::FBPaginatedArray( _parameters(Parameters), _objectFactory(ObjectFactory) { + if (_parameters == nullptr) + { + _parameters = ref new PropertySet(); + } } Windows::Foundation::IAsyncOperation^ FBPaginatedArray::FirstAsync( @@ -130,8 +134,7 @@ IVectorView^ FBPaginatedArray::ObjectArrayFromJsonArray( } FBResult^ FBPaginatedArray::ConsumePagedResponse( - String^ JsonText, - String^ OriginalPath + String^ JsonText ) { FBResult^ result = nullptr; @@ -163,7 +166,7 @@ FBResult^ FBPaginatedArray::ConsumePagedResponse( if (_paging) { foundPaging = true; - FormatPagingPaths(_paging, OriginalPath); + FormatPagingPaths(_paging); } } else if (!String::CompareOrdinal(it->Current->Key, L"data")) @@ -232,10 +235,10 @@ Windows::Foundation::IAsyncOperation^ FBPaginatedArray::GetPage( String^ path ) { - return create_async([this, path]() -> task + return create_async([=]() -> task { - return create_task(HttpManager::Instance->GetTaskAsync(path, _parameters)) - .then([this, path](String^ responseString) + return create_task(HttpManager::Instance->GetTaskAsync(path, _parameters->GetView())) + .then([this](String^ responseString) { if (responseString == nullptr) { @@ -243,18 +246,17 @@ Windows::Foundation::IAsyncOperation^ FBPaginatedArray::GetPage( } else { - return ConsumePagedResponse(responseString, path); + return ConsumePagedResponse(responseString); } }); }); } void FBPaginatedArray::FormatPagingPaths( - FBPaging^ paging, - String^ path + FBPaging^ paging ) { - String^ regexString = L"(?:.*?)(" + path + L".*)"; + String^ regexString = L"(?:.*?)(" + _request + L".*)"; std::wstring stdRegexString = std::wstring{ regexString->Data() }; std::wregex relativePathRegex{ stdRegexString }; std::wsmatch match; @@ -275,7 +277,7 @@ void FBPaginatedArray::FormatPagingPaths( if (match.size() >= 2) { std::wstring matchText = match[1].str(); - paging->Next = ref new String(matchText.c_str()); + paging->Previous = ref new String(matchText.c_str()); } } } \ No newline at end of file diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.h b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.h index be4d9a7..4b3ddbe 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.h +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.h @@ -143,8 +143,7 @@ namespace winsdkfb ); FBResult^ ConsumePagedResponse( - Platform::String^ JsonText, - Platform::String^ OriginalPath + Platform::String^ JsonText ); Windows::Foundation::IAsyncOperation^ GetPage( @@ -152,8 +151,7 @@ namespace winsdkfb ); void FormatPagingPaths( - FBPaging^ paging, - Platform::String^ path + FBPaging^ paging ); Windows::Foundation::Collections::IVectorView^ _current; diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/HttpManager.cpp b/winsdkfb/winsdkfb/winsdkfb.Shared/HttpManager.cpp index 965730f..e8ed697 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/HttpManager.cpp +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/HttpManager.cpp @@ -34,23 +34,23 @@ void HttpManager::SetHttpClient(IHttpClient^ httpClient) _httpClient = httpClient; } -IAsyncOperation^ HttpManager::GetTaskAsync(String^ path, PropertySet^ parameters) +IAsyncOperation^ HttpManager::GetTaskAsync(String^ path, IMapView^ parameters) { return _httpClient->GetTaskAsync(path, parameters); } -IAsyncOperation^ HttpManager::PostTaskAsync(String^ path, PropertySet^ parameters) +IAsyncOperation^ HttpManager::PostTaskAsync(String^ path, IMapView^ parameters) { return _httpClient->PostTaskAsync(path, parameters); } -IAsyncOperation^ HttpManager::DeleteTaskAsync(String^ path, PropertySet^ parameters) +IAsyncOperation^ HttpManager::DeleteTaskAsync(String^ path, IMapView^ parameters) { return _httpClient->DeleteTaskAsync(path, parameters); } String^ HttpManager::ParametersToQueryString( - PropertySet^ Parameters + IMapView^ Parameters ) { return _httpClient->ParametersToQueryString(Parameters); diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/HttpManager.h b/winsdkfb/winsdkfb/winsdkfb.Shared/HttpManager.h index a40c19e..631e374 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/HttpManager.h +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/HttpManager.h @@ -34,21 +34,21 @@ namespace winsdkfb virtual Windows::Foundation::IAsyncOperation^ GetTaskAsync( Platform::String^ path, - Windows::Foundation::Collections::PropertySet^ parameters + Windows::Foundation::Collections::IMapView^ parameters ); virtual Windows::Foundation::IAsyncOperation^ PostTaskAsync( Platform::String^ path, - Windows::Foundation::Collections::PropertySet^ parameters + Windows::Foundation::Collections::IMapView^ parameters ); virtual Windows::Foundation::IAsyncOperation^ DeleteTaskAsync( Platform::String^ path, - Windows::Foundation::Collections::PropertySet^ parameters + Windows::Foundation::Collections::IMapView^ parameters ); virtual Platform::String^ ParametersToQueryString( - Windows::Foundation::Collections::PropertySet^ Parameters + Windows::Foundation::Collections::IMapView^ parameters ); private: diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/IHttpClient.h b/winsdkfb/winsdkfb/winsdkfb.Shared/IHttpClient.h index 49b1326..fdd68d1 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/IHttpClient.h +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/IHttpClient.h @@ -23,21 +23,21 @@ namespace winsdkfb public: Windows::Foundation::IAsyncOperation^ GetTaskAsync( Platform::String^ path, - Windows::Foundation::Collections::PropertySet^ parameters + Windows::Foundation::Collections::IMapView^ parameters ); Windows::Foundation::IAsyncOperation^ PostTaskAsync( Platform::String^ path, - Windows::Foundation::Collections::PropertySet^ parameters + Windows::Foundation::Collections::IMapView^ parameters ); Windows::Foundation::IAsyncOperation^ DeleteTaskAsync( Platform::String^ path, - Windows::Foundation::Collections::PropertySet^ parameters + Windows::Foundation::Collections::IMapView^ parameters ); Platform::String^ ParametersToQueryString( - Windows::Foundation::Collections::PropertySet^ Parameters + Windows::Foundation::Collections::IMapView^ parameters ); }; } diff --git a/winsdkfb/winsdkfb_uwp/winsdkfb_uwp/FBAppEvents/FacebookAppEvents.cpp b/winsdkfb/winsdkfb_uwp/winsdkfb_uwp/FBAppEvents/FacebookAppEvents.cpp index b7be3d5..11b1fc5 100644 --- a/winsdkfb/winsdkfb_uwp/winsdkfb_uwp/FBAppEvents/FacebookAppEvents.cpp +++ b/winsdkfb/winsdkfb_uwp/winsdkfb_uwp/FBAppEvents/FacebookAppEvents.cpp @@ -170,7 +170,7 @@ IAsyncOperation^ FBSDKAppEvents::LogInstallEvent( parameters->Insert(L"windows_attribution_id", campaignID); return create_task([=]() -> IAsyncOperation^ { - return HttpManager::Instance->PostTaskAsync(path, parameters); + return HttpManager::Instance->PostTaskAsync(path, parameters->GetView()); }); } catch (Platform::Exception^ ex) @@ -206,7 +206,7 @@ IAsyncAction^ FBSDKAppEvents::LogActivateEvent( return create_async([=]() { - return create_task(HttpManager::Instance->PostTaskAsync(path, parameters)) + return create_task(HttpManager::Instance->PostTaskAsync(path, parameters->GetView())) .then([=](String^ response) -> void { #ifdef _DEBUG From ca479109f8db4d6fb6fb6ec91c3ba0cd31b44556 Mon Sep 17 00:00:00 2001 From: Austin Diviness Date: Tue, 11 Oct 2016 13:26:48 -0700 Subject: [PATCH 10/11] refactored uri building into its own class --- .../winsdkfb.Shared/FacebookClient.cpp | 308 ++++-------------- .../winsdkfb/winsdkfb.Shared/FacebookClient.h | 17 +- .../FacebookPaginatedArray.cpp | 31 -- .../winsdkfb.Shared/FacebookPaginatedArray.h | 4 - .../winsdkfb.Shared/GraphUriBuilder.cpp | 145 +++++++++ .../winsdkfb.Shared/GraphUriBuilder.h | 42 +++ .../winsdkfb.Shared/winsdkfb.Shared.vcxitems | 2 + .../winsdkfb.Shared.vcxitems.filters | 6 + 8 files changed, 276 insertions(+), 279 deletions(-) create mode 100644 winsdkfb/winsdkfb/winsdkfb.Shared/GraphUriBuilder.cpp create mode 100644 winsdkfb/winsdkfb/winsdkfb.Shared/GraphUriBuilder.h diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.cpp b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.cpp index f213b9b..a83b80c 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.cpp +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.cpp @@ -23,6 +23,7 @@ #include "HttpMethod.h" #include "JsonClassFactory.h" #include "SDKMessage.h" +#include "GraphUriBuilder.h" using namespace concurrency; using namespace Platform; @@ -103,19 +104,19 @@ IAsyncOperation^ FBClient::GetTaskAsync( IAsyncOperation^ myTask = create_async([=]() { Uri^ uri = FBClient::PrepareRequestUri(HttpMethod::Get, path, - modifiableParams, nullptr); + modifiableParams); return FBClient::GetTaskInternalAsync(uri) .then([=](String^ Response) { task result; - if (FBClient::IsOAuthErrorResponse(Response)) + if (FBClient::IsOAuthErrorResponse(Response)) { result = create_task([=]() { FBSession^ sess = FBSession::ActiveSession; - return FBSession::ActiveSession->TryRefreshAccessToken(); + return FBSession::ActiveSession->TryRefreshAccessToken(); }) .then([=](FBResult^ Result) { @@ -187,7 +188,7 @@ IAsyncOperation^ FBClient::SimplePostAsync( return create_async([=]() { Uri^ uri = FBClient::PrepareRequestUri(HttpMethod::Post, path, - parameters, nullptr); + parameters); return FBClient::SimplePostInternalAsync(uri) .then([=](String^ Response) @@ -199,7 +200,7 @@ IAsyncOperation^ FBClient::SimplePostAsync( result = create_task([=]() { FBSession^ sess = FBSession::ActiveSession; - return FBSession::ActiveSession->TryRefreshAccessToken(); + return FBSession::ActiveSession->TryRefreshAccessToken(); }) .then([=](FBResult^ Result) { @@ -267,7 +268,7 @@ IAsyncOperation^ FBClient::MultipartPostAsync( return create_async([=]() { Uri^ uri = FBClient::PrepareRequestUri(HttpMethod::Post, path, - parameters, nullptr); + parameters); return FBClient::MultipartPostInternalAsync(uri, streams) .then([=](String^ Response) @@ -279,7 +280,7 @@ IAsyncOperation^ FBClient::MultipartPostAsync( result = create_task([=]() { FBSession^ sess = FBSession::ActiveSession; - return FBSession::ActiveSession->TryRefreshAccessToken(); + return FBSession::ActiveSession->TryRefreshAccessToken(); }) .then([=](FBResult^ Result) { @@ -337,7 +338,7 @@ IAsyncOperation^ FBClient::PostTaskAsync( } Windows::Foundation::IAsyncOperation^ FBClient::DeleteTaskAsync( - String^ path, + String^ path, IMapView^ parameters ) { @@ -345,7 +346,7 @@ Windows::Foundation::IAsyncOperation^ FBClient::DeleteTaskAsync( return create_async([=]() { Uri^ uri = FBClient::PrepareRequestUri(HttpMethod::Delete, path, - modifiableParams, nullptr); + modifiableParams); return FBClient::DeleteTaskInternalAsync(uri) .then([=](String^ Response) @@ -357,7 +358,7 @@ Windows::Foundation::IAsyncOperation^ FBClient::DeleteTaskAsync( result = create_task([=]() { FBSession^ sess = FBSession::ActiveSession; - return FBSession::ActiveSession->TryRefreshAccessToken(); + return FBSession::ActiveSession->TryRefreshAccessToken(); }) .then([=](FBResult^ Result) { @@ -392,41 +393,37 @@ task FBClient::DeleteTaskInternalAsync( } Uri^ FBClient::PrepareRequestUri( - winsdkfb::HttpMethod httpMethod, - String^ path, - PropertySet^ parameters, - Windows::Storage::Streams::IRandomAccessStream^ input + winsdkfb::HttpMethod httpMethod, + String^ path, + PropertySet^ parameters ) { FBSession^ sess = FBSession::ActiveSession; - - // Setup datawriter for the InMemoryRandomAccessStream - DataWriter^ dataWriter = ref new DataWriter(input); - dataWriter->UnicodeEncoding = UnicodeEncoding::Utf8; - dataWriter->ByteOrder = ByteOrder::LittleEndian; + GraphUriBuilder^ uriBuilder = ref new GraphUriBuilder(path); if (parameters == nullptr) { parameters = ref new PropertySet(); } - // remove any query string parameters from path and put them in parameters - path = MovePathQueryStringToParameters(path, parameters); PropertySet^ mediaObjects = ref new PropertySet(); PropertySet^ mediaStreams = ref new PropertySet(); PropertySet^ parametersWithoutMediaObjects = ToDictionary(parameters, mediaObjects, mediaStreams); + // ensure that media items are in valid states + ValidateMediaStreams(mediaStreams); + ValidateMediaObjects(mediaObjects); if (parametersWithoutMediaObjects == nullptr) { parametersWithoutMediaObjects = ref new PropertySet(); } - if (!parametersWithoutMediaObjects->HasKey("access_token") && + if (!parametersWithoutMediaObjects->HasKey("access_token") && (sess->AccessTokenData != nullptr) && (sess->AccessTokenData->AccessToken->Data() != nullptr) && (sess->AccessTokenData->AccessToken->Length() > 0)) { - parametersWithoutMediaObjects->Insert("access_token", + parametersWithoutMediaObjects->Insert("access_token", sess->AccessTokenData->AccessToken); } @@ -435,211 +432,71 @@ Uri^ FBClient::PrepareRequestUri( parametersWithoutMediaObjects->Insert("format", "json-strings"); } - String^ contentTypeHeader = MultiPartContentType; - String^ boundary = ref new String(MultiPartBoundary); - contentTypeHeader += "boundary=" + boundary + MultiPartNewLine; - long contentLength = -1; - String^ queryString = ref new String(); - - OutputDebugString(contentTypeHeader->Data()); - SerializeParameters(parametersWithoutMediaObjects); - if (parametersWithoutMediaObjects->HasKey("access_token")) + // Add remaining parameters to query string. Note that parameters that + // do not need to be uploaded as multipart, i.e. any which is are not + // binary data, are required to be in the query string, even for POST + // requests! + auto kvp = parametersWithoutMediaObjects->First(); + while (kvp->HasCurrent) { - // Add access_token to query string as the first parameter, for our own - // convenience. We could as easily add it in the while loop below, but - // putting at the beginning of the query string is helpful for - // debugging, etc. - auto accessToken = dynamic_cast( - parametersWithoutMediaObjects->Lookup("access_token")); - if ((accessToken != nullptr) && (accessToken->Length() > 0) && - (accessToken != "null")) - { - queryString += "access_token=" + Uri::EscapeComponent(accessToken); - } + uriBuilder->AddQueryParam(kvp->Current->Key, static_cast(kvp->Current->Value)); + kvp->MoveNext(); + } - // Remove the token before we loop through and add general parameters - parametersWithoutMediaObjects->Remove("access_token"); - - // Add remaining parameters to query string. Note that parameters that - // do not need to be uploaded as multipart, i.e. any which is are not - // binary data, are required to be in the query string, even for POST - // requests! - auto kvp = parametersWithoutMediaObjects->First(); - while (kvp->HasCurrent) - { - String^ key = Uri::EscapeComponent(kvp->Current->Key); - String^ value = Uri::EscapeComponent( - dynamic_cast(kvp->Current->Value)); + return uriBuilder->MakeUri(); +} - if (queryString->Length() > 0) +void FBClient::ValidateMediaStreams(PropertySet^ mediaStreams) +{ + if (mediaStreams->Size > 0) + { + IIterator^>^ facebookMediaStream = mediaStreams->First(); + while(facebookMediaStream->HasCurrent) + { + FBMediaStream^ mediaStream = dynamic_cast(facebookMediaStream->Current->Value); + if ((mediaStream->Stream == nullptr) || + (mediaStream->Stream->ContentType == nullptr) || + (mediaStream->FileName == nullptr) || + (mediaStream->FileName->Length() == 0)) { - queryString += "&"; + throw ref new InvalidArgumentException(AttachmentMustHavePropertiesSetError); } - queryString += key + L"=" + value; - - kvp->MoveNext(); - } - - if (mediaStreams->Size > 0) - { - IIterator^>^ facebookMediaStream = - mediaStreams->First(); - while(facebookMediaStream->HasCurrent) + IRandomAccessStream^ stream = mediaStream->Stream; + if (stream == nullptr) { - String^ sbMediaStream = ref new String(); - FBMediaStream^ mediaStream = - dynamic_cast( - facebookMediaStream->Current->Value); + throw ref new InvalidArgumentException(AttachmentValueIsNull); + } + facebookMediaStream->MoveNext(); + } + } +} - if ((mediaStream->Stream == nullptr) || - (mediaStream->Stream->ContentType == nullptr) || - (mediaStream->FileName == nullptr) || - (mediaStream->FileName->Length() == 0)) - { - throw ref new InvalidArgumentException(AttachmentMustHavePropertiesSetError); - } - - sbMediaStream = MultiPartFormPrefix + boundary + - MultiPartNewLine + - "Content-Disposition: form-data; name=\"" + - facebookMediaStream->Current->Key + "\"; filename=\"" + - mediaStream->FileName + "\"" + MultiPartNewLine + - "Content-Type: " + mediaStream->Stream->ContentType + - MultiPartNewLine + MultiPartNewLine; - OutputDebugString(sbMediaStream->Data()); - OutputDebugString(L"\n"); - - dataWriter->WriteString(sbMediaStream); - - IRandomAccessStream^ stream = mediaStream->Stream; - if (stream == nullptr) - { - throw ref new InvalidArgumentException(AttachmentValueIsNull); - } - - IInputStream^ inputStream = stream->GetInputStreamAt(0); - DataReader^ dataReader = ref new DataReader(inputStream); - dataReader->UnicodeEncoding = UnicodeEncoding::Utf8; - dataReader->ByteOrder = ByteOrder::LittleEndian; - - dataWriter->WriteBuffer(dataReader->ReadBuffer(dataReader->UnconsumedBufferLength)); - - dataWriter->WriteString(MultiPartNewLine); - facebookMediaStream->MoveNext(); +void FBClient::ValidateMediaObjects(PropertySet^ mediaObjects) +{ + if (mediaObjects->Size > 0) + { + IIterator^>^ facebookMediaObject = mediaObjects->First(); + while (facebookMediaObject->HasCurrent) + { + FBMediaObject^ mediaObject = dynamic_cast(facebookMediaObject->Current->Value); + if ((mediaObject->GetValue()== nullptr) || + (mediaObject->ContentType == nullptr) || + (mediaObject->FileName == nullptr) || + (mediaObject->FileName->Length() == 0)) + { + throw ref new InvalidArgumentException(AttachmentMustHavePropertiesSetError); } - String^ str = ref new String(); - str = MultiPartNewLine + MultiPartFormPrefix + boundary + - MultiPartFormPrefix + MultiPartNewLine; - - dataWriter->WriteString(str); - } - - if (mediaObjects->Size > 0) - { - IIterator^>^ facebookMediaObject = - mediaObjects->First(); - while (facebookMediaObject->HasCurrent) + if (mediaObject->GetValue() == nullptr) { - String^ sbMediaObject = ref new String(); - FBMediaObject^ mediaObject = - dynamic_cast( - facebookMediaObject->Current->Value); - - if ((mediaObject->GetValue()== nullptr) || - (mediaObject->ContentType == nullptr) || - (mediaObject->FileName == nullptr) || - (mediaObject->FileName->Length() == 0)) - { - throw ref new InvalidArgumentException(AttachmentMustHavePropertiesSetError); - } - - sbMediaObject = MultiPartFormPrefix + boundary + - MultiPartNewLine + - "Content-Disposition: form-data; name=\"" + - facebookMediaObject->Current->Key + "\"; filename=\"" + - mediaObject->FileName + "\"" + MultiPartNewLine + - "Content-Type: " + mediaObject->ContentType + - MultiPartNewLine + MultiPartNewLine; - OutputDebugString(sbMediaObject->Data()); - OutputDebugString(L"\n"); - - dataWriter->WriteString(sbMediaObject); - - if (mediaObject->GetValue() == nullptr) - { - throw ref new InvalidArgumentException(AttachmentValueIsNull); - } - - dataWriter->WriteBytes(mediaObject->GetValue()); - - dataWriter->WriteString(MultiPartNewLine); - facebookMediaObject->MoveNext(); + throw ref new InvalidArgumentException(AttachmentValueIsNull); } - - String^ str = ref new String(); - str = MultiPartNewLine + MultiPartFormPrefix + boundary + - MultiPartFormPrefix + MultiPartNewLine; - - dataWriter->WriteString(str); - } - - // TODO: Figure out where to get the right value for this. input - //doesn't appear to have a length at this point in the code when debugging, - contentLength = input == nullptr ? 0 : (long)input->Size; - } - else - { - // for GET,DELETE - if (mediaObjects->Size > 0 && mediaStreams->Size > 0) - { - throw ref new FailureException("Attachments (FBMediaObject/FBMediaStream) are valid only in POST requests."); - } - #if !WP8 - if (httpMethod == HttpMethod::Delete) - { - queryString += L"method=delete&"; - } - #endif - queryString += ParametersToQueryString(parametersWithoutMediaObjects->GetView()); - } - - String^ host; - String^ apiVersion = L""; - - if (parametersWithoutMediaObjects->HasKey("request_host")) - { - host = static_cast( - parametersWithoutMediaObjects->Lookup("request_host")); - } - else - { - host = L"graph.facebook.com"; - if (sess->APIMajorVersion) - { - apiVersion = L"v" + sess->APIMajorVersion.ToString() + L"." + sess->APIMinorVersion.ToString() + L"/"; + facebookMediaObject->MoveNext(); } } - - // Check the path for multiple id read requests and - // modify it accordingly - const std::wstring wStringPath(path->Data()); - std::size_t found = wStringPath.find(L"?ids="); - if (found != std::string::npos) - { - path += L"&"; - } - else - { - path += L"?"; - } - - String^ uriString = L"https://" + host + L"/" + apiVersion + path + queryString; - - return ref new Uri(uriString); } void FBClient::SerializeParameters( @@ -681,9 +538,9 @@ String^ FBClient::ParametersToQueryString( { String^ queryString = L""; - // Add remaining parameters to query string. Note that parameters that - // do not need to be uploaded as multipart, i.e. any which is are not - // binary data, are required to be in the query string, even for POST + // Add remaining parameters to query string. Note that parameters that + // do not need to be uploaded as multipart, i.e. any which is are not + // binary data, are required to be in the query string, even for POST // requests! IIterator^>^ kvp = parameters->First(); while (kvp->HasCurrent) @@ -759,27 +616,6 @@ task FBClient::TryReceiveHttpResponse( }); } -String^ FBClient::MovePathQueryStringToParameters(String^ path, PropertySet^ parameters) -{ - Uri^ uri = ref new Uri(L"http://blah.com/" + path); - WwwFormUrlDecoder^ query = uri->QueryParsed; - if (query->Size > 0) - { - IIterator^ it = query->First(); - while (it->HasCurrent) - { - IWwwFormUrlDecoderEntry^ current = it->Current; - // we don't want to overwrite an explicitly specified value from the user - if (!parameters->HasKey(current->Name)) - { - parameters->Insert(current->Name, current->Value); - } - it->MoveNext(); - } - } - return uri->Path; -} - PropertySet^ FBClient::MapViewToPropertySet(IMapView^ mapView) { PropertySet^ propertySet = ref new PropertySet(); diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.h b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.h index e3698b3..3685de8 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.h +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookClient.h @@ -156,7 +156,6 @@ namespace winsdkfb * @param httpMethod Type of HTTP request to build URI for * @param path Request path * @param parameters Query parameters for the request - * @param input TODO * @return Request URI * @exception FailureExecption if FBMediaObject or FBMediaStream are * attempting to be attached on non-POST requests. @@ -165,8 +164,7 @@ namespace winsdkfb Windows::Foundation::Uri^ PrepareRequestUri( winsdkfb::HttpMethod httpMethod, Platform::String^ path, - Windows::Foundation::Collections::PropertySet^ parameters, - Windows::Storage::Streams::IRandomAccessStream^ input + Windows::Foundation::Collections::PropertySet^ parameters ); /** @@ -228,11 +226,6 @@ namespace winsdkfb Windows::Foundation::Collections::PropertySet^ Streams ); - Platform::String^ FBClient::MovePathQueryStringToParameters( - Platform::String^ path, - Windows::Foundation::Collections::PropertySet^ parameters - ); - concurrency::task TryReceiveHttpResponse( concurrency::task httpRequestTask, concurrency::cancellation_token_source cancellationTokenSource @@ -241,5 +234,13 @@ namespace winsdkfb Windows::Foundation::Collections::PropertySet^ MapViewToPropertySet( Windows::Foundation::Collections::IMapView^ mapView ); + + void ValidateMediaStreams( + Windows::Foundation::Collections::PropertySet^ mediaStreams + ); + + void ValidateMediaObjects( + Windows::Foundation::Collections::PropertySet^ mediaObjects + ); }; }; diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.cpp b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.cpp index 74a986e..0f6ba63 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.cpp +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.cpp @@ -166,7 +166,6 @@ FBResult^ FBPaginatedArray::ConsumePagedResponse( if (_paging) { foundPaging = true; - FormatPagingPaths(_paging); } } else if (!String::CompareOrdinal(it->Current->Key, L"data")) @@ -250,34 +249,4 @@ Windows::Foundation::IAsyncOperation^ FBPaginatedArray::GetPage( } }); }); -} - -void FBPaginatedArray::FormatPagingPaths( - FBPaging^ paging -) -{ - String^ regexString = L"(?:.*?)(" + _request + L".*)"; - std::wstring stdRegexString = std::wstring{ regexString->Data() }; - std::wregex relativePathRegex{ stdRegexString }; - std::wsmatch match; - if (paging->Next) - { - std::wstring nextUrl = std::wstring{ paging->Next->Data() }; - std::regex_match(nextUrl, match, relativePathRegex); - if (match.size() >= 2) - { - std::wstring matchText = match[1].str(); - paging->Next = ref new String(matchText.c_str()); - } - } - if (paging->Previous) - { - std::wstring previousUrl = std::wstring{ paging->Previous->Data() }; - std::regex_match(previousUrl, match, relativePathRegex); - if (match.size() >= 2) - { - std::wstring matchText = match[1].str(); - paging->Previous = ref new String(matchText.c_str()); - } - } } \ No newline at end of file diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.h b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.h index 4b3ddbe..b83dad2 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.h +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/FacebookPaginatedArray.h @@ -150,10 +150,6 @@ namespace winsdkfb Platform::String^ path ); - void FormatPagingPaths( - FBPaging^ paging - ); - Windows::Foundation::Collections::IVectorView^ _current; FBPaging^ _paging; Platform::String^ _request; diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/GraphUriBuilder.cpp b/winsdkfb/winsdkfb/winsdkfb.Shared/GraphUriBuilder.cpp new file mode 100644 index 0000000..c167e12 --- /dev/null +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/GraphUriBuilder.cpp @@ -0,0 +1,145 @@ +//****************************************************************************** +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//****************************************************************************** + +#include "pch.h" +#include "GraphUriBuilder.h" +#include "FacebookSession.h" + +#include +#include + +using namespace Platform; +using namespace Windows::Foundation; +using namespace Windows::Foundation::Collections; +using namespace winsdkfb; + + +GraphUriBuilder::GraphUriBuilder(String^ path) + : + _queryParams { ref new PropertySet() } +{ + Uri^ testUri; + bool buildDomain = false; + try + { + testUri = ref new Uri(path); + } + catch (Platform::InvalidArgumentException^ e) + { + buildDomain = true; + } + + if (buildDomain) + { + String^ domain = L"https://graph.facebook.com/"; + testUri = ref new Uri(domain + path); + } + _host = testUri->Host; + _path = testUri->Path; + _scheme = testUri->SchemeName; + + // remove any extra '/'s from path + FixPathDelimiters(); + // capture api version from path or build it + BuildApiVersionString(); + // save query params in map for later use + DecodeQueryParams(testUri); +} + +void GraphUriBuilder::AddQueryParam(Platform::String^ query, Platform::String^ param) +{ + _queryParams->Insert(query, param); +} + +Windows::Foundation::Uri^ GraphUriBuilder::MakeUri() +{ + // this request_host stuff is a bit of a hack to send graph calls in + // the unit tests to our own web service. + if (_queryParams->HasKey(L"request_host")) + { + _host = static_cast(_queryParams->Lookup(L"request_host")); + } + String^ fullPath = _scheme + L"://" + _host + L"/" + _apiVersion + _path; + if (_queryParams->Size > 0) + { + fullPath += L"?"; + while (_queryParams->Size > 1) + { + auto it = _queryParams->First(); + fullPath += Uri::EscapeComponent(it->Current->Key) + L"=" + Uri::EscapeComponent(static_cast(it->Current->Value)) + L"&"; + _queryParams->Remove(it->Current->Key); + } + auto it = _queryParams->First(); + fullPath += Uri::EscapeComponent(it->Current->Key) + L"=" + Uri::EscapeComponent(static_cast(it->Current->Value)); + } + return ref new Uri(fullPath); +} + +void GraphUriBuilder::BuildApiVersionString() +{ + std::wstring regexString = LR"__(^.?(v\d\.\d)(.*))__"; + std::wregex apiRegex{ regexString }; + std::wsmatch match; + std::wstring searchString{ _path->Data() }; + std::regex_match(searchString, match, apiRegex); + if (match.size() >= 3) // 1 for matched string + 2 for each capture group + { + std::wstring apiString = match[1].str(); + _apiVersion = ref new String(apiString.c_str()); + // need to adjust _path so that the api version doesn't get added twice + std::wstring newPath = match[2].str(); + _path = ref new String(newPath.c_str()); + } + else + { + FBSession^ sess = FBSession::ActiveSession; + if (sess->APIMajorVersion) + { + _apiVersion = "v" + sess->APIMajorVersion.ToString() + "." + sess->APIMinorVersion.ToString(); + } + } +} + +void GraphUriBuilder::FixPathDelimiters() +{ + String^ fixedPath = L""; + std::wstring originalPath{ _path->Data() }; + std::wistringstream iss{ originalPath }; + std::wstring token; + while (std::getline(iss, token, L'/')) + { + if (token.size() > 0) + { + fixedPath += L"/" + ref new String(token.c_str()); + } + } + _path = fixedPath; +} + +void GraphUriBuilder::DecodeQueryParams(Uri^ uri) +{ + WwwFormUrlDecoder^ decoder = uri->QueryParsed; + if (decoder->Size > 0) + { + IIterator^ it = decoder->First(); + while (it->HasCurrent) + { + IWwwFormUrlDecoderEntry^ current = it->Current; + _queryParams->Insert(current->Name, current->Value); + it->MoveNext(); + } + } +} \ No newline at end of file diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/GraphUriBuilder.h b/winsdkfb/winsdkfb/winsdkfb.Shared/GraphUriBuilder.h new file mode 100644 index 0000000..18971c5 --- /dev/null +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/GraphUriBuilder.h @@ -0,0 +1,42 @@ +//****************************************************************************** +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//****************************************************************************** + +#pragma once + +#include + +namespace winsdkfb +{ + public ref class GraphUriBuilder sealed + { + public: + GraphUriBuilder(Platform::String^ path); + + Windows::Foundation::Uri^ MakeUri(); + void AddQueryParam(Platform::String^ query, Platform::String^ param); + + private: + void BuildApiVersionString(); + void FixPathDelimiters(); + void DecodeQueryParams(Windows::Foundation::Uri^ uri); + + Platform::String^ _host; + Platform::String^ _path; + Platform::String^ _scheme; + Platform::String^ _apiVersion; + Windows::Foundation::Collections::PropertySet^ _queryParams; + }; +} diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/winsdkfb.Shared.vcxitems b/winsdkfb/winsdkfb/winsdkfb.Shared/winsdkfb.Shared.vcxitems index e385d9c..161dbfc 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/winsdkfb.Shared.vcxitems +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/winsdkfb.Shared.vcxitems @@ -56,6 +56,7 @@ + @@ -96,6 +97,7 @@ Create + diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/winsdkfb.Shared.vcxitems.filters b/winsdkfb/winsdkfb/winsdkfb.Shared/winsdkfb.Shared.vcxitems.filters index 6cf1e5d..6d5a251 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/winsdkfb.Shared.vcxitems.filters +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/winsdkfb.Shared.vcxitems.filters @@ -85,6 +85,9 @@ Source Files + + Source Files + @@ -189,6 +192,9 @@ Header Files + + Header Files + From 8b7fa51b37d41b6a7155a34aa1bca7e4df807a17 Mon Sep 17 00:00:00 2001 From: Austin Diviness Date: Wed, 19 Oct 2016 12:17:48 -0700 Subject: [PATCH 11/11] reworked graph uri query string param generation --- .../winsdkfb/winsdkfb.Shared/GraphUriBuilder.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/winsdkfb/winsdkfb/winsdkfb.Shared/GraphUriBuilder.cpp b/winsdkfb/winsdkfb/winsdkfb.Shared/GraphUriBuilder.cpp index c167e12..0b17f0b 100644 --- a/winsdkfb/winsdkfb/winsdkfb.Shared/GraphUriBuilder.cpp +++ b/winsdkfb/winsdkfb/winsdkfb.Shared/GraphUriBuilder.cpp @@ -73,17 +73,17 @@ Windows::Foundation::Uri^ GraphUriBuilder::MakeUri() _host = static_cast(_queryParams->Lookup(L"request_host")); } String^ fullPath = _scheme + L"://" + _host + L"/" + _apiVersion + _path; + if (_queryParams->Size > 0) { - fullPath += L"?"; - while (_queryParams->Size > 1) - { - auto it = _queryParams->First(); - fullPath += Uri::EscapeComponent(it->Current->Key) + L"=" + Uri::EscapeComponent(static_cast(it->Current->Value)) + L"&"; - _queryParams->Remove(it->Current->Key); - } + String^ separator = L"?"; auto it = _queryParams->First(); - fullPath += Uri::EscapeComponent(it->Current->Key) + L"=" + Uri::EscapeComponent(static_cast(it->Current->Value)); + while (it->HasCurrent) + { + fullPath += separator + Uri::EscapeComponent(it->Current->Key) + L"=" + Uri::EscapeComponent(static_cast(it->Current->Value)); + separator = L"&"; + it->MoveNext(); + } } return ref new Uri(fullPath); }