Merge pull request #217 from Microsoft/develop

Develop merge
This commit is contained in:
adiviness 2016-10-20 10:21:06 -07:00 коммит произвёл GitHub
Родитель 1707d96539 751451d8dd
Коммит cfa49c33a7
25 изменённых файлов: 480 добавлений и 272 удалений

Просмотреть файл

@ -210,14 +210,14 @@
<ItemGroup>
<PRIResource Include="Resources.resw" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\..\winsdkfb\winsdkfb_testing_key.pfx" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\winsdkfb\winsdkfb_uwp\winsdkfb_uwp\winsdkfb_uwp.vcxproj">
<Project>{973a943b-ff77-4267-8f30-f5fe2b7f5583}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="..\..\..\winsdkfb\winsdkfb_testing_key.pfx" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>

Просмотреть файл

@ -34,7 +34,6 @@
<ClCompile Include="FBPageBindable.cpp" />
<ClCompile Include="UserInfo.xaml.cpp" />
<ClCompile Include="UserLikes.xaml.cpp" />
<ClCompile Include="OptionsPage.xaml.cpp" />
<ClCompile Include="Dialogs.xaml.cpp" />
</ItemGroup>
<ItemGroup>
@ -47,7 +46,6 @@
<ClInclude Include="FBPageBindable.h" />
<ClInclude Include="UserInfo.xaml.h" />
<ClInclude Include="UserLikes.xaml.h" />
<ClInclude Include="OptionsPage.xaml.h" />
<ClInclude Include="Dialogs.xaml.h" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -114,13 +114,18 @@
<None Include="..\winsdkfb_testing_key.pfx">
<Link>winsdkfb_testing_key.pfx</Link>
</None>
<None Include="data.json" />
<None Include="FBCSObjectImplementation.ttinclude" />
<None Include="FBPhoto.tt" />
<None Include="FBObject.tt" />
<None Include="FBSuccess.tt" />
<None Include="FBTestUser.tt" />
<None Include="packages.config" />
<Content Include="TestData\testJsonPropertySet.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="TestData\testJsonPropertySetNoBuiltInFields.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="Utility.ttinclude" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -29,7 +29,7 @@ namespace FBWinStoreCsTests
{
public String ResponseData;
public IAsyncOperation<string> DeleteTaskAsync(string path, PropertySet parameters)
public IAsyncOperation<string> DeleteTaskAsync(string path, IReadOnlyDictionary<String, Object> parameters)
{
return Task.Run(() =>
{
@ -37,7 +37,7 @@ namespace FBWinStoreCsTests
}).AsAsyncOperation<string>();
}
public IAsyncOperation<string> GetTaskAsync(string path, PropertySet parameters)
public IAsyncOperation<string> GetTaskAsync(string path, IReadOnlyDictionary<String, Object> parameters)
{
return Task.Run(() =>
{
@ -45,12 +45,12 @@ namespace FBWinStoreCsTests
}).AsAsyncOperation<string>();
}
public string ParametersToQueryString(PropertySet Parameters)
public string ParametersToQueryString(IReadOnlyDictionary<String, Object> Parameters)
{
throw new NotImplementedException();
}
public IAsyncOperation<string> PostTaskAsync(string path, PropertySet parameters)
public IAsyncOperation<string> PostTaskAsync(string path, IReadOnlyDictionary<String, Object> parameters)
{
return Task.Run(() =>
{

Просмотреть файл

@ -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"
}
}

Просмотреть файл

@ -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"
}
}

Просмотреть файл

@ -15,6 +15,7 @@
//******************************************************************************
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -700,5 +701,49 @@ 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""}");
}
[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);
}
}
}

Просмотреть файл

Просмотреть файл

@ -89,6 +89,10 @@ namespace winsdkfb
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 winsdkfb
<#
}
#>
Windows::Foundation::Collections::PropertySet^ _fields;
};
}
}

Просмотреть файл

@ -38,7 +38,7 @@ using namespace winsdkfb::Graph;
using namespace Platform;
using namespace Windows::Data::Json;
using namespace Windows::Foundation::Collections;
<#
<#
if (bindable)
{
#>
@ -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++)
{
@ -118,7 +122,7 @@ void <#= className #>::<#= propName #>::set(<#= rtType #> value)
}
#>
Object^ <#= className #>::FromJson(
String^ JsonText
String^ JsonText
)
{
<#= className #>^ result = ref new <#= className #>;
@ -178,11 +182,38 @@ 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);
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;
}
@ -190,7 +221,7 @@ Object^ <#= className #>::FromJson(
}
return result;
}
<#
<#
if (bindable)
{
#>
@ -200,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);

Просмотреть файл

@ -146,13 +146,13 @@ Windows::Foundation::IAsyncOperation<FBResult^>^ 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");

Просмотреть файл

@ -23,6 +23,7 @@
#include "HttpMethod.h"
#include "JsonClassFactory.h"
#include "SDKMessage.h"
#include "GraphUriBuilder.h"
using namespace concurrency;
using namespace Platform;
@ -96,25 +97,26 @@ PropertySet^ FBClient::ToDictionary(PropertySet^ parameters, PropertySet^ mediaO
IAsyncOperation<String^>^ FBClient::GetTaskAsync(
String^ path,
PropertySet^ parameters
IMapView<String^, Object^>^ parameters
)
{
PropertySet^ modifiableParams = MapViewToPropertySet(parameters);
IAsyncOperation<String^>^ myTask = create_async([=]()
{
Uri^ uri = FBClient::PrepareRequestUri(HttpMethod::Get, path,
parameters, nullptr);
modifiableParams);
return FBClient::GetTaskInternalAsync(uri)
.then([=](String^ Response)
{
task<String^> 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)
{
@ -186,7 +188,7 @@ IAsyncOperation<String^>^ FBClient::SimplePostAsync(
return create_async([=]()
{
Uri^ uri = FBClient::PrepareRequestUri(HttpMethod::Post, path,
parameters, nullptr);
parameters);
return FBClient::SimplePostInternalAsync(uri)
.then([=](String^ Response)
@ -198,7 +200,7 @@ IAsyncOperation<String^>^ FBClient::SimplePostAsync(
result = create_task([=]()
{
FBSession^ sess = FBSession::ActiveSession;
return FBSession::ActiveSession->TryRefreshAccessToken();
return FBSession::ActiveSession->TryRefreshAccessToken();
})
.then([=](FBResult^ Result)
{
@ -266,7 +268,7 @@ IAsyncOperation<String^>^ FBClient::MultipartPostAsync(
return create_async([=]()
{
Uri^ uri = FBClient::PrepareRequestUri(HttpMethod::Post, path,
parameters, nullptr);
parameters);
return FBClient::MultipartPostInternalAsync(uri, streams)
.then([=](String^ Response)
@ -278,7 +280,7 @@ IAsyncOperation<String^>^ FBClient::MultipartPostAsync(
result = create_task([=]()
{
FBSession^ sess = FBSession::ActiveSession;
return FBSession::ActiveSession->TryRefreshAccessToken();
return FBSession::ActiveSession->TryRefreshAccessToken();
})
.then([=](FBResult^ Result)
{
@ -318,32 +320,33 @@ task<String^> FBClient::MultipartPostInternalAsync(
IAsyncOperation<String^>^ FBClient::PostTaskAsync(
String^ path,
PropertySet^ parameters
IMapView<String^, Object^>^ parameters
)
{
PropertySet^ modifiableParams = MapViewToPropertySet(parameters);
IAsyncOperation<String^>^ 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<String^>^ FBClient::DeleteTaskAsync(
String^ path,
PropertySet^ parameters
String^ path,
IMapView<String^, Object^>^ parameters
)
{
PropertySet^ modifiableParams = MapViewToPropertySet(parameters);
return create_async([=]()
{
Uri^ uri = FBClient::PrepareRequestUri(HttpMethod::Delete, path,
parameters, nullptr);
modifiableParams);
return FBClient::DeleteTaskInternalAsync(uri)
.then([=](String^ Response)
@ -355,7 +358,7 @@ Windows::Foundation::IAsyncOperation<String^>^ FBClient::DeleteTaskAsync(
result = create_task([=]()
{
FBSession^ sess = FBSession::ActiveSession;
return FBSession::ActiveSession->TryRefreshAccessToken();
return FBSession::ActiveSession->TryRefreshAccessToken();
})
.then([=](FBResult^ Result)
{
@ -390,34 +393,37 @@ task<String^> 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;
GraphUriBuilder^ uriBuilder = ref new GraphUriBuilder(path);
// Setup datawriter for the InMemoryRandomAccessStream
DataWriter^ dataWriter = ref new DataWriter(input);
dataWriter->UnicodeEncoding = UnicodeEncoding::Utf8;
dataWriter->ByteOrder = ByteOrder::LittleEndian;
if (parameters == nullptr)
{
parameters = ref new PropertySet();
}
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);
}
@ -426,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<String^>(
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<String^>(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<String^>(kvp->Current->Value));
return uriBuilder->MakeUri();
}
if (queryString->Length() > 0)
void FBClient::ValidateMediaStreams(PropertySet^ mediaStreams)
{
if (mediaStreams->Size > 0)
{
IIterator<IKeyValuePair<String^, Object^>^>^ facebookMediaStream = mediaStreams->First();
while(facebookMediaStream->HasCurrent)
{
FBMediaStream^ mediaStream = dynamic_cast<FBMediaStream^>(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<IKeyValuePair<String^, Object^>^>^ facebookMediaStream =
mediaStreams->First();
while(facebookMediaStream->HasCurrent)
IRandomAccessStream^ stream = mediaStream->Stream;
if (stream == nullptr)
{
String^ sbMediaStream = ref new String();
FBMediaStream^ mediaStream =
dynamic_cast<FBMediaStream^>(
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<IKeyValuePair<String^, Object^>^>^ facebookMediaObject = mediaObjects->First();
while (facebookMediaObject->HasCurrent)
{
FBMediaObject^ mediaObject = dynamic_cast<FBMediaObject^>(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<IKeyValuePair<String^, Object^>^>^ facebookMediaObject =
mediaObjects->First();
while (facebookMediaObject->HasCurrent)
if (mediaObject->GetValue() == nullptr)
{
String^ sbMediaObject = ref new String();
FBMediaObject^ mediaObject =
dynamic_cast<FBMediaObject^>(
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);
}
String^ host;
String^ apiVersion = L"";
if (parametersWithoutMediaObjects->HasKey("request_host"))
{
host = static_cast<String^>(
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(
@ -654,8 +520,12 @@ void FBClient::SerializeParameters(
auto item = keysThatAreNotString->First();
while (item->HasCurrent)
{
// TODO: Jsonize the object value
String^ newValue = dynamic_cast<String^>(parameters->Lookup(item->Current));
Object^ val = parameters->Lookup(item->Current);
String^ newValue = dynamic_cast<String^>(val);
if (!newValue)
{
newValue = val->ToString();
}
// Replace the existing object with the new Jsonized value
parameters->Remove(item->Current);
@ -667,16 +537,16 @@ void FBClient::SerializeParameters(
}
String^ FBClient::ParametersToQueryString(
PropertySet^ Parameters
IMapView<String^, Object^>^ parameters
)
{
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<IKeyValuePair<String^, Object^>^>^ kvp = Parameters->First();
IIterator<IKeyValuePair<String^, Object^>^>^ kvp = parameters->First();
while (kvp->HasCurrent)
{
String^ key = Uri::EscapeComponent(kvp->Current->Key);
@ -749,3 +619,16 @@ task<String^> FBClient::TryReceiveHttpResponse(
return result;
});
}
PropertySet^ FBClient::MapViewToPropertySet(IMapView<String^, Object^>^ mapView)
{
PropertySet^ propertySet = ref new PropertySet();
IIterator<IKeyValuePair<String^, Object^>^>^ it = mapView->First();
while (it->HasCurrent)
{
IKeyValuePair<String^, Object^>^ current = it->Current;
propertySet->Insert(current->Key, current->Value);
it->MoveNext();
}
return propertySet;
}

Просмотреть файл

@ -42,7 +42,7 @@ namespace winsdkfb
*/
GetTaskAsync(
Platform::String^ path,
Windows::Foundation::Collections::PropertySet^ parameters
Windows::Foundation::Collections::IMapView<Platform::String^, Platform::Object^>^ parameters
);
/**
@ -56,7 +56,7 @@ namespace winsdkfb
virtual Windows::Foundation::IAsyncOperation<Platform::String^>^
PostTaskAsync(
Platform::String^ path,
Windows::Foundation::Collections::PropertySet^ parameters
Windows::Foundation::Collections::IMapView<Platform::String^, Platform::Object^>^ parameters
);
/**
@ -70,7 +70,7 @@ namespace winsdkfb
virtual Windows::Foundation::IAsyncOperation<Platform::String^>^
DeleteTaskAsync(
Platform::String^ path,
Windows::Foundation::Collections::PropertySet^ parameters
Windows::Foundation::Collections::IMapView<Platform::String^, Platform::Object^>^ 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<Platform::String^, Platform::Object^>^ parameters
);
private:
@ -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
);
/**
@ -232,5 +230,17 @@ namespace winsdkfb
concurrency::task<Windows::Web::Http::HttpResponseMessage^> httpRequestTask,
concurrency::cancellation_token_source cancellationTokenSource
);
Windows::Foundation::Collections::PropertySet^ MapViewToPropertySet(
Windows::Foundation::Collections::IMapView<Platform::String^, Platform::Object^>^ mapView
);
void ValidateMediaStreams(
Windows::Foundation::Collections::PropertySet^ mediaStreams
);
void ValidateMediaObjects(
Windows::Foundation::Collections::PropertySet^ mediaObjects
);
};
};

Просмотреть файл

@ -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;

Просмотреть файл

@ -21,6 +21,8 @@
#include "FacebookSession.h"
#include "HttpManager.h"
#include <regex>
using namespace concurrency;
using namespace winsdkfb;
using namespace winsdkfb::Graph;
@ -42,6 +44,10 @@ FBPaginatedArray::FBPaginatedArray(
_parameters(Parameters),
_objectFactory(ObjectFactory)
{
if (_parameters == nullptr)
{
_parameters = ref new PropertySet();
}
}
Windows::Foundation::IAsyncOperation<FBResult^>^ FBPaginatedArray::FirstAsync(
@ -228,9 +234,9 @@ Windows::Foundation::IAsyncOperation<FBResult^>^ FBPaginatedArray::GetPage(
String^ path
)
{
return create_async([this, path]() -> task<FBResult^>
return create_async([=]() -> task<FBResult^>
{
return create_task(HttpManager::Instance->GetTaskAsync(path, _parameters))
return create_task(HttpManager::Instance->GetTaskAsync(path, _parameters->GetView()))
.then([this](String^ responseString)
{
if (responseString == nullptr)
@ -243,4 +249,4 @@ Windows::Foundation::IAsyncOperation<FBResult^>^ FBPaginatedArray::GetPage(
}
});
});
}
}

Просмотреть файл

@ -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
@ -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()
@ -205,9 +208,12 @@ task<FBResult^> 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);
@ -961,7 +967,6 @@ IAsyncOperation<FBResult^>^ FBSession::LoginAsync(
{
Permissions = ref new FBPermissions((ref new Vector<String^>())->GetView());
}
_dialog = ref new FacebookDialog();
return create_async([=]()
{

Просмотреть файл

@ -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 <regex>
#include <sstream>
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<String^>(_queryParams->Lookup(L"request_host"));
}
String^ fullPath = _scheme + L"://" + _host + L"/" + _apiVersion + _path;
if (_queryParams->Size > 0)
{
String^ separator = L"?";
auto it = _queryParams->First();
while (it->HasCurrent)
{
fullPath += separator + Uri::EscapeComponent(it->Current->Key) + L"=" + Uri::EscapeComponent(static_cast<String^>(it->Current->Value));
separator = L"&";
it->MoveNext();
}
}
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<IWwwFormUrlDecoderEntry^>^ it = decoder->First();
while (it->HasCurrent)
{
IWwwFormUrlDecoderEntry^ current = it->Current;
_queryParams->Insert(current->Name, current->Value);
it->MoveNext();
}
}
}

Просмотреть файл

@ -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 <ppltasks.h>
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;
};
}

Просмотреть файл

@ -34,23 +34,23 @@ void HttpManager::SetHttpClient(IHttpClient^ httpClient)
_httpClient = httpClient;
}
IAsyncOperation<String^>^ HttpManager::GetTaskAsync(String^ path, PropertySet^ parameters)
IAsyncOperation<String^>^ HttpManager::GetTaskAsync(String^ path, IMapView<String^, Object^>^ parameters)
{
return _httpClient->GetTaskAsync(path, parameters);
}
IAsyncOperation<String^>^ HttpManager::PostTaskAsync(String^ path, PropertySet^ parameters)
IAsyncOperation<String^>^ HttpManager::PostTaskAsync(String^ path, IMapView<String^, Object^>^ parameters)
{
return _httpClient->PostTaskAsync(path, parameters);
}
IAsyncOperation<String^>^ HttpManager::DeleteTaskAsync(String^ path, PropertySet^ parameters)
IAsyncOperation<String^>^ HttpManager::DeleteTaskAsync(String^ path, IMapView<String^, Object^>^ parameters)
{
return _httpClient->DeleteTaskAsync(path, parameters);
}
String^ HttpManager::ParametersToQueryString(
PropertySet^ Parameters
IMapView<String^, Object^>^ Parameters
)
{
return _httpClient->ParametersToQueryString(Parameters);

Просмотреть файл

@ -34,21 +34,21 @@ namespace winsdkfb
virtual Windows::Foundation::IAsyncOperation<Platform::String^>^ GetTaskAsync(
Platform::String^ path,
Windows::Foundation::Collections::PropertySet^ parameters
Windows::Foundation::Collections::IMapView<Platform::String^, Platform::Object^>^ parameters
);
virtual Windows::Foundation::IAsyncOperation<Platform::String^>^ PostTaskAsync(
Platform::String^ path,
Windows::Foundation::Collections::PropertySet^ parameters
Windows::Foundation::Collections::IMapView<Platform::String^, Platform::Object^>^ parameters
);
virtual Windows::Foundation::IAsyncOperation<Platform::String^>^ DeleteTaskAsync(
Platform::String^ path,
Windows::Foundation::Collections::PropertySet^ parameters
Windows::Foundation::Collections::IMapView<Platform::String^, Platform::Object^>^ parameters
);
virtual Platform::String^ ParametersToQueryString(
Windows::Foundation::Collections::PropertySet^ Parameters
Windows::Foundation::Collections::IMapView<Platform::String^, Platform::Object^>^ parameters
);
private:

Просмотреть файл

@ -23,21 +23,21 @@ namespace winsdkfb
public:
Windows::Foundation::IAsyncOperation<Platform::String^>^ GetTaskAsync(
Platform::String^ path,
Windows::Foundation::Collections::PropertySet^ parameters
Windows::Foundation::Collections::IMapView<Platform::String^, Platform::Object^>^ parameters
);
Windows::Foundation::IAsyncOperation<Platform::String^>^ PostTaskAsync(
Platform::String^ path,
Windows::Foundation::Collections::PropertySet^ parameters
Windows::Foundation::Collections::IMapView<Platform::String^, Platform::Object^>^ parameters
);
Windows::Foundation::IAsyncOperation<Platform::String^>^ DeleteTaskAsync(
Platform::String^ path,
Windows::Foundation::Collections::PropertySet^ parameters
Windows::Foundation::Collections::IMapView<Platform::String^, Platform::Object^>^ parameters
);
Platform::String^ ParametersToQueryString(
Windows::Foundation::Collections::PropertySet^ Parameters
Windows::Foundation::Collections::IMapView<Platform::String^, Platform::Object^>^ parameters
);
};
}

Просмотреть файл

@ -56,6 +56,7 @@
<ClInclude Include="$(MSBuildThisFileDirectory)pch.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)ScaleConverter.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)SDKMessage.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)GraphUriBuilder.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(MSBuildThisFileDirectory)ColorLuminosityConverter.cpp" />
@ -96,6 +97,7 @@
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="$(MSBuildThisFileDirectory)ScaleConverter.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)GraphUriBuilder.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectCapability Include="SourceItemsFromImports" />

Просмотреть файл

@ -85,6 +85,9 @@
<ClCompile Include="$(MSBuildThisFileDirectory)HttpManager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(MSBuildThisFileDirectory)GraphUriBuilder.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(MSBuildThisFileDirectory)FacebookDialog.xaml.h" />
@ -189,6 +192,9 @@
<ClInclude Include="$(MSBuildThisFileDirectory)FBConstants.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(MSBuildThisFileDirectory)GraphUriBuilder.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="$(MSBuildThisFileDirectory)Images\fb_blank_profile_portrait.png" />

Просмотреть файл

@ -98,6 +98,10 @@
<None Include="..\..\FBWinStoreCsTests\FBCSObjectImplementation.ttinclude">
<Link>FBCSObjectImplementation.ttinclude</Link>
</None>
<Content Include="..\..\FBWinStoreCsTests\TestData\testJsonPropertySet.json">
<Link>TestData\testJsonPropertySet.json</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="..\..\FBWinStoreCsTests\Utility.ttinclude">
<Link>Utility.ttinclude</Link>
</None>

Просмотреть файл

@ -170,7 +170,7 @@ IAsyncOperation<String^>^ FBSDKAppEvents::LogInstallEvent(
parameters->Insert(L"windows_attribution_id", campaignID);
return create_task([=]() -> IAsyncOperation<String^>^
{
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