diff --git a/JenkinsConsoleUtility/jcuSrc/Commands/VersionVarWriter.cs b/JenkinsConsoleUtility/jcuSrc/Commands/VersionVarWriter.cs
index 5ef97b9d4..b3f45ce71 100644
--- a/JenkinsConsoleUtility/jcuSrc/Commands/VersionVarWriter.cs
+++ b/JenkinsConsoleUtility/jcuSrc/Commands/VersionVarWriter.cs
@@ -139,6 +139,7 @@ private string GetSdkGenKey(string sdkName)
case "xplatcppsdk": case "xplatbetasdk": case "xplatcppsdk-private-switch": case "xplatcppsdk-private-ps4": case "xplatcppsdk-private-gdk": return "xplatcppsdk";
case "javasdk": case "javabetasdk": return "java";
case "pythonsdk": case "pythonbetasdk": return "pythonsdk";
+ case "restclientcollection": return "restclient";
default: return sdkName.ToLower(); // Most new sdks have matching names
}
diff --git a/SDKBuildScripts/Linux/rest_client_build.sh b/SDKBuildScripts/Linux/rest_client_build.sh
new file mode 100644
index 000000000..d28cbc4cf
--- /dev/null
+++ b/SDKBuildScripts/Linux/rest_client_build.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+SdkName="RESTClientCollection"
+delSrc=false
+
+cd ..
+. ./shared_build.sh
\ No newline at end of file
diff --git a/SDKBuildScripts/Windows/rest_client_build.bat b/SDKBuildScripts/Windows/rest_client_build.bat
new file mode 100644
index 000000000..f6ab84893
--- /dev/null
+++ b/SDKBuildScripts/Windows/rest_client_build.bat
@@ -0,0 +1,5 @@
+set SdkName=RESTClientCollection
+set delSrc=false
+
+cd ..
+call shared_build.bat
diff --git a/SDKBuildScripts/shared_build.bat b/SDKBuildScripts/shared_build.bat
index 42cfaf3b1..d2b00a28f 100644
--- a/SDKBuildScripts/shared_build.bat
+++ b/SDKBuildScripts/shared_build.bat
@@ -5,6 +5,7 @@ if [%delSrc%] == [true] (
del /S *.cpp
del /S *.cs
del /S *.h
+ del /S *.http
del /S *.java
del /S *.js
del /S *.lua
diff --git a/SDKBuildScripts/shared_build.sh b/SDKBuildScripts/shared_build.sh
index 2ca48b49d..fedca758f 100644
--- a/SDKBuildScripts/shared_build.sh
+++ b/SDKBuildScripts/shared_build.sh
@@ -54,6 +54,7 @@ CleanCodeFiles () {
NukeAll "*.cpp"
NukeAll "*.cs"
NukeAll "*.h"
+ NukeAll "*.http"
NukeAll "*.java"
NukeAll "*.js"
NukeAll "*.lua"
diff --git a/SDKGenerator.njsproj b/SDKGenerator.njsproj
index f9bd08c8c..8ab4e3f81 100644
--- a/SDKGenerator.njsproj
+++ b/SDKGenerator.njsproj
@@ -86,6 +86,11 @@
bin\
-destpath ..\sdks\PythonSdk -apiSpecGitUrl -buildIdentifier PythonSdk_Manual
+
+ true
+ bin\
+ -destpath ..\sdks\RESTClientCollection -apiSpecGitUrl -buildIdentifier RESTClientCollection_manual
+
true
bin\
@@ -445,6 +450,12 @@
+
+
+ Code
+
+
+
@@ -937,6 +948,8 @@
+
+
@@ -1070,6 +1083,8 @@
+
+
diff --git a/SDKGenerator.sln b/SDKGenerator.sln
index a962aa067..c385afc05 100644
--- a/SDKGenerator.sln
+++ b/SDKGenerator.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27130.2027
+# Visual Studio Version 17
+VisualStudioVersion = 17.11.35303.130
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}") = "SDKGenerator", "SDKGenerator.njsproj", "{2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}"
EndProject
@@ -17,12 +17,13 @@ Global
NodeSDK|Any CPU = NodeSDK|Any CPU
ObjCSdk|Any CPU = ObjCSdk|Any CPU
PhpSdk|Any CPU = PhpSdk|Any CPU
+ PlayFabCoreCSdk|Any CPU = PlayFabCoreCSdk|Any CPU
+ PlayFabServicesCSdk|Any CPU = PlayFabServicesCSdk|Any CPU
PostmanCollection|Any CPU = PostmanCollection|Any CPU
Python|Any CPU = Python|Any CPU
+ RESTClientCollection|Any CPU = RESTClientCollection|Any CPU
UnitySdk|Any CPU = UnitySdk|Any CPU
Unreal|Any CPU = Unreal|Any CPU
- PlayFabCoreCSdk|Any CPU = PlayFabCoreCSdk|Any CPU
- PlayFabServicesCSdk|Any CPU = PlayFabServicesCSdk|Any CPU
XPlatCppSdk|Any CPU = XPlatCppSdk|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
@@ -46,18 +47,20 @@ Global
{2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.ObjCSdk|Any CPU.Build.0 = ObjCSdk|Any CPU
{2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.PhpSdk|Any CPU.ActiveCfg = PhpSdk|Any CPU
{2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.PhpSdk|Any CPU.Build.0 = PhpSdk|Any CPU
+ {2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.PlayFabCoreCSdk|Any CPU.ActiveCfg = PlayFabCoreCSdk|Any CPU
+ {2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.PlayFabCoreCSdk|Any CPU.Build.0 = PlayFabCoreCSdk|Any CPU
+ {2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.PlayFabServicesCSdk|Any CPU.ActiveCfg = PlayFabServicesCSdk|Any CPU
+ {2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.PlayFabServicesCSdk|Any CPU.Build.0 = PlayFabServicesCSdk|Any CPU
{2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.PostmanCollection|Any CPU.ActiveCfg = PostmanCollection|Any CPU
{2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.PostmanCollection|Any CPU.Build.0 = PostmanCollection|Any CPU
{2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.Python|Any CPU.ActiveCfg = Python|Any CPU
{2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.Python|Any CPU.Build.0 = Python|Any CPU
+ {2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.RESTClientCollection|Any CPU.ActiveCfg = RESTClientCollection|Any CPU
+ {2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.RESTClientCollection|Any CPU.Build.0 = RESTClientCollection|Any CPU
{2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.UnitySdk|Any CPU.ActiveCfg = UnitySdk|Any CPU
{2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.UnitySdk|Any CPU.Build.0 = UnitySdk|Any CPU
{2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.Unreal|Any CPU.ActiveCfg = Unreal|Any CPU
{2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.Unreal|Any CPU.Build.0 = Unreal|Any CPU
- {2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.PlayFabCoreCSdk|Any CPU.ActiveCfg = PlayFabCoreCSdk|Any CPU
- {2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.PlayFabCoreCSdk|Any CPU.Build.0 = PlayFabCoreCSdk|Any CPU
- {2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.PlayFabServicesCSdk|Any CPU.ActiveCfg = PlayFabServicesCSdk|Any CPU
- {2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.PlayFabServicesCSdk|Any CPU.Build.0 = PlayFabServicesCSdk|Any CPU
{2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.XPlatCppSdk|Any CPU.ActiveCfg = XPlatCppSdk|Any CPU
{2BA807EF-310C-43B5-8D42-1ABCFFF39BE9}.XPlatCppSdk|Any CPU.Build.0 = XPlatCppSdk|Any CPU
EndGlobalSection
diff --git a/targets/restclient/make.js b/targets/restclient/make.js
new file mode 100644
index 000000000..4722af924
--- /dev/null
+++ b/targets/restclient/make.js
@@ -0,0 +1,165 @@
+const path = require("path");
+
+// Making resharper less noisy - These are defined in Generate.js
+if (typeof (getCompiledTemplate) === "undefined") getCompiledTemplate = () => { };
+if (typeof (templatizeTree) === "undefined") templatizeTree = () => { };
+
+let propertyReplacements = {};
+
+// generate.js looks for some specific exported functions (as defined in TOC.json) in make.js, like:
+exports.makeCombinedAPI = (apis, sourceDir, apiOutputDir) => {
+ // Builds every api. The provided "apis" variable is a list of objects, Examples: API_SPECS/Legacy/PlayFab/admin.api.json, API_SPECS/Legacy/PlayFab/server.api.json, and API_SPECS/Legacy/PlayFab/client.api.json
+
+ console.log("Generating Combined api from: " + sourceDir + " to: " + apiOutputDir);
+
+ try {
+ propertyReplacements = require(path.resolve(sourceDir, "replacements.json"));
+ } catch (ex) {
+ throw "The file: replacements.json was not properly formatted JSON";
+ }
+
+ const envTemplate = getCompiledTemplate(path.resolve(sourceDir, "templates/settings.json.ejs"));
+ writeFile(path.resolve(apiOutputDir, ".vscode/settings.json"), envTemplate());
+
+ // Filter empty apis
+ apis = apis.filter(api => api.calls.length > 0);
+
+ var groupedApis = {};
+ apis.forEach(api => {
+ if (!groupedApis[api.name]) {
+ groupedApis[api.name] = [];
+ }
+ api.calls.forEach(call => {
+ var key = call.subgroup;
+ if (!groupedApis[api.name][key]) {
+ groupedApis[api.name][key] = [];
+ }
+ groupedApis[api.name][key].push(call);
+ });
+ });
+
+ const locals = {
+ apis: groupedApis,
+ sdkVersion: sdkGlobals.sdkVersion,
+ fixRequestExample: fixRequestExample,
+ getBaseUrl: getBaseUrl,
+ getHeaders: getHeaders,
+ getVariables: getVariables,
+ getVerticalTag: getVerticalTag
+ };
+
+ const template = getCompiledTemplate(path.resolve(sourceDir, "templates/playfab.http.ejs"));
+ const generatedTemplateText = template(locals);
+ writeFile(path.resolve(apiOutputDir, "playfab.http"), generatedTemplateText);
+}
+
+const checkReplacements = (apiName, apiCallName, obj) => {
+ for (let replaceCategory in propertyReplacements) {
+ if (replaceCategory === "generic") {
+ for (let genReplaceName1 in propertyReplacements[replaceCategory])
+ doReplace(obj, genReplaceName1, propertyReplacements[replaceCategory][genReplaceName1]);
+ }
+ if (replaceCategory === apiName) {
+ for (let apiReplaceName in propertyReplacements[replaceCategory]) {
+ if (apiReplaceName === "generic") {
+ for (let genReplaceName2 in propertyReplacements[replaceCategory][apiReplaceName])
+ doReplace(obj, genReplaceName2, propertyReplacements[replaceCategory][apiReplaceName][genReplaceName2]);
+ }
+ if (apiReplaceName === apiCallName) {
+ for (let apiCallReplaceName in propertyReplacements[replaceCategory][apiReplaceName])
+ doReplace(obj, apiCallReplaceName, propertyReplacements[replaceCategory][apiReplaceName][apiCallReplaceName]);
+ }
+ }
+ }
+ }
+}
+
+const doReplace = (obj, paramName, newValue) => {
+ if (obj.hasOwnProperty(paramName)) {
+ console.log("Replaced: " + obj[paramName] + " with " + JSON.stringify(newValue));
+ if (typeof newValue !== 'object' || Array.isArray(newValue)) {
+ obj[paramName] = newValue;
+ return;
+ }
+
+ Object.keys(newValue).forEach(key => {
+ if (!!obj[paramName][key]) obj[paramName][key] = newValue[key];
+ });
+ }
+};
+
+const fixRequestExample = (apiName, apiCallName, example) => {
+ if (example) {
+ let output = JSON.parse(example);
+ checkReplacements(apiName, apiCallName, output);
+ if (Object.keys(output).length === 0) return "";
+ return JSON.stringify(output, undefined, 2);
+ }
+ return example;
+}
+
+const getBaseUrl = () => {
+ if (sdkGlobals.verticalName) {
+ // verticalName isn't an established variable in Postman, and we know it here, so we can just apply it
+ return "https://" + sdkGlobals.verticalName + ".{{domain}}";
+ }
+ return "https://{{titleId}}.{{domain}}";
+}
+
+const getHeaders = (apiCall) => {
+ let headers = [];
+ headers.push('Accept-Encoding: gzip');
+ headers.push('Content-Type: application/json');
+ headers.push(`X-PlayFabSDK: RESTClientCollection-${sdkGlobals.sdkVersion}`);
+ if (apiCall.url === "/Authentication/GetEntityToken") {
+ headers.push('X-Authorization: {{sessionTicket}}');
+ headers.push('X-SecretKey: {{secretKey}}');
+ return headers.join('\n');
+ }
+
+ if (apiCall.auth === "SessionTicket") {
+ headers.push('X-Authorization: {{sessionTicket}}');
+ return headers.join('\n');
+ }
+
+ if (apiCall.auth === "SecretKey") {
+ headers.push('X-SecretKey: {{secretKey}}');
+ return headers.join('\n');
+ }
+
+ if (apiCall.auth === "EntityToken") {
+ headers.push('X-EntityToken: {{entityToken}}');
+ return headers.join('\n');
+ }
+
+ return headers.join('\n');
+}
+
+const getVariables = () => {
+ let variables = [];
+ variables.push("@entityToken = {{GetEntityToken.response.body.data.EntityToken}}");
+ variables.push("# LoginWithCustomID can be replaced by other authentication methods (e.g., LoginWithFacebook)");
+ variables.push("@sessionTicket = {{LoginWithCustomID.response.body.data.SessionTicket}}");
+ variables.push("@playFabId = {{LoginWithCustomID.response.body.data.PlayFabId}}");
+ variables.push("@titlePlayerAccountId = {{GetAccountInfo.response.body.data.AccountInfo.TitleInfo.TitlePlayerAccount.Id}}");
+ variables.push("@characterId = {{GrantCharacterToUser.response.body.data.CharacterId}}");
+ variables.push("@newsId = {{AddNews.response.body.data.NewsId}}");
+ variables.push("@sharedSecretKey = {{CreatePlayerSharedSecret.response.body.data.SecretKey}}");
+ variables.push("@segmentId = {{GetAllSegments.response.body.data.Segments[0].Id }}");
+ variables.push("@taskId = {{CreateCloudScriptTask.response.body.data.TaskId}}");
+ variables.push("#@taskId = {{CreateActionsOnPlayersInSegmentTask.response.body.data.TaskId}}");
+ variables.push("@taskInstanceId = {{RunTask.response.body.data.TaskInstanceId}}");
+ variables.push("@gameServerId = {{AuthenticateGameServerWithCustomId.response.body.data.EntityToken.Entity.Id}}");
+ variables.push("@profileExpectedVersion = {{GetPlayerProfile.response.body.data.Profile.VersionNumber}}");
+ variables.push("@segmentExportId = {{ExportPlayersInSegment.response.body.data.ExportId}}");
+ variables.push("@policyExpectedVersion = {{GetPolicy.response.body.data.PolicyVersion}}");
+ return variables.join('\n');
+}
+
+const getVerticalTag = () => {
+ if (sdkGlobals.verticalName) {
+ return " for vertical: " + sdkGlobals.verticalName;
+ }
+
+ return "";
+}
\ No newline at end of file
diff --git a/targets/restclient/replacements.json b/targets/restclient/replacements.json
new file mode 100644
index 000000000..741ee99b2
--- /dev/null
+++ b/targets/restclient/replacements.json
@@ -0,0 +1,116 @@
+{
+ "generic": {
+ "CatalogVersion": "{{primaryCatalogName}}",
+ "CharacterId": "{{characterId}}",
+ "ConnectionId": "{{connectionId}}",
+ "EntityId": "{{entityId}}",
+ "EntityToken": "{{entityToken}}",
+ "PlayFabId": "{{playFabId}}",
+ "TitleId": "{{titleId}}"
+ },
+ "Admin": {
+ "generic": {
+ "NewsId": "{{newsId}}",
+ "Parameter": {
+ "SegmentId": "{{segmentId}}"
+ },
+ "SecretKey": "{{sharedSecretKey}}",
+ "SegmentId": "{{segmentId}}",
+ "StatisticName": "{{statisticName}}",
+ "TaskId": "{{taskId}}",
+ "TaskInstanceId": "{{taskInstanceId}}",
+ "VirtualCurrency": "{{currencyCode}}"
+ },
+ "DeleteTask": {
+ "Identifier": {
+ "Id": "{{taskId}}"
+ }
+ },
+ "GetSegmentExport": {
+ "ExportId": "{{segmentExportId}}"
+ },
+ "RunTask": {
+ "Identifier": {
+ "Id": "{{taskId}}"
+ }
+ }
+ },
+ "Authentication": {
+ "generic": {
+ "CustomId": "{{serverCustomId}}"
+ },
+ "Delete": {
+ "Entity": {
+ "Id": "{{gameServerId}}"
+ }
+ }
+ },
+ "Client": {
+ "generic": {
+ "CreateAccount": true,
+ "CustomId": "{{userCustomId}}",
+ "FriendPlayFabId": "{{friendPlayFabId}}",
+ "PlayFabIds": [
+ "{{playFabId}}"
+ ],
+ "SharedGroupId": "{{sharedGroupId}}"
+ }
+ },
+ "CloudScript": {
+ "generic": {
+ "Entity": {
+ "Id": "{{titlePlayerAccountId}}"
+ }
+ },
+ "PostFunctionResultForPlayerTriggeredAction": {
+ "PlayStreamEventEnvelope": {
+ "EntityId": "{{titlePlayerAccountId}}"
+ },
+ "PlayerProfile": {
+ "TitleId": "{{titleId}}",
+ "PlayerId": "{{titlePlayerAccountId}}"
+ }
+ }
+ },
+ "Data": {
+ "generic": {
+ "Entity": {
+ "Id": "{{titlePlayerAccountId}}"
+ },
+ "ExpectedVersion": "profileExptectedVersion"
+ }
+ },
+ "Multiplayer": {
+ "generic": {
+ "Entity": {
+ "Id": "{{titlePlayerAccountId}}"
+ },
+ "ServerEntity": {
+ "Id": "{{gameServerId}}"
+ }
+ }
+ },
+ "Profiles": {
+ "generic": {
+ "Entity": {
+ "Id": "{{titlePlayerAccountId}}"
+ }
+ },
+ "GetTitlePlayersFromMasterPlayerAccountIds": {
+ "MasterPlayerAccountIds": [
+ "{{playFabId}}"
+ ]
+ },
+ "GetProfiles": {
+ "Entities": [
+ {
+ "Id": "{{titlePlayerAccountId}}",
+ "Type": "title_player_account",
+ "TypeString": "title_player_account"
+ }
+ ]
+ }
+ },
+ "Server": {
+ }
+}
\ No newline at end of file
diff --git a/targets/restclient/templates/playfab.http.ejs b/targets/restclient/templates/playfab.http.ejs
new file mode 100644
index 000000000..381277df2
--- /dev/null
+++ b/targets/restclient/templates/playfab.http.ejs
@@ -0,0 +1,22 @@
+# PlayFab REST Client SDK <%- sdkVersion %><%- getVerticalTag() %>
+
+<%- getVariables() %>
+<% for(var a in apis) {
+ var api = apis[a];%>
+###### <%- a %> ######
+<% for(var g in api) {
+ var subgroup = api[g]; %>
+##### <%- g %> #####
+<% for(var c in subgroup) {
+ var apiCall = subgroup[c]; %>
+###
+# @name <%- apiCall.name %>
+# summary: <%- apiCall.summary %>
+# request details: <%- apiCall.requestDetails %>
+<%- apiCall.method %> <%- getBaseUrl() %><%- `${apiCall.url}?sdk=RESTClientCollection-${sdkVersion}` %>
+<%- getHeaders(apiCall) %>
+
+<%- fixRequestExample(a, apiCall.name, apiCall.requestExample) %>
+<% }
+ }
+} %>
\ No newline at end of file
diff --git a/targets/restclient/templates/settings.json.ejs b/targets/restclient/templates/settings.json.ejs
new file mode 100644
index 000000000..9b0e67630
--- /dev/null
+++ b/targets/restclient/templates/settings.json.ejs
@@ -0,0 +1,17 @@
+{
+ "rest-client.environmentVariables": {
+ "$shared": {
+ "connectionId": "exampleprovider",
+ "currencyCode": "GC",
+ "domain": "playfabapi.com",
+ "friendPlayFabId": "",
+ "primaryCatalogName": "products",
+ "secretKey": "",
+ "serverCustomId": "server12345678123412341234123456789abc",
+ "sharedGroupId": "Clan Data",
+ "statisticName": "headshots",
+ "titleId": "",
+ "userCustomId": "user12345678123412341234123456789abc"
+ }
+ }
+}
\ No newline at end of file