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