using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.Networking;
namespace Unity.Services.Mediation.Dashboard.Editor
{
static class DashboardClient
{
const string k_ErrorProjectNotConfigured = "To access the full suite of Mediation Editor tools, please link your project to a Unity Dashboard project under Edit > Project Settings > Services";
const string k_ErrorFailedToRetrieveAdUnits = "Failed to retrieve Ad Units from the Dashboard; ";
const string k_ErrorFailedToRetrieveGameId = "Failed to retrieve Game IDs from the Dashboard; ";
const string k_ErrorFailedToRetrieveAdNetworks = "Failed to retrieve Configured ad Networks from the Dashboard; ";
const string k_AdUnitsListUrl = "https://services.unity.com/api/monetize/mediation/v1/organizations/{0}/projects/{1}/ad-units";
const string k_GameIdUrl = "https://services.unity.com/api/monetize/mediation/v1/organizations/{0}/projects/{1}/app-adnetwork-parameters/{2}";
const string k_AdNetworksListUrl = "https://services.unity.com/api/monetize/mediation/v1/organizations/{0}/ad-networks";
const string k_AdNetworkUnity = "UNITY";
static DashboardPoller s_ProjectSettingsPoller;
static DashboardPoller s_NoTimeoutPoller;
static DashboardClient()
{
s_ProjectSettingsPoller = new DashboardPoller(IsCloudProjectSettingsValid);
s_NoTimeoutPoller = new DashboardPoller(IsCloudProjectSettingsValid, 1.0, Int32.MaxValue);
}
static bool IsCloudProjectSettingsValid()
{
return !string.IsNullOrEmpty(CloudProjectSettings.projectId)
&& !string.IsNullOrEmpty(CloudProjectSettings.userId)
&& !string.IsNullOrEmpty(CloudProjectSettings.accessToken)
&& !string.IsNullOrEmpty(Core.Editor.OrganizationHandler.OrganizationProvider.Organization.Key);
}
///
/// Validates that the Cloud project settings are properly set so we can access the Dashboard
///
/// True if we have the proper info to access the dashboard
static void IsProjectReadyForConfigurationRequests(Action callback)
{
var isCloudProjectSettingsValid = IsCloudProjectSettingsValid();
if (isCloudProjectSettingsValid || s_ProjectSettingsPoller.FinishedPolling)
{
callback?.Invoke(isCloudProjectSettingsValid);
}
else
{
s_ProjectSettingsPoller.AddCallback(callback);
}
}
///
/// Retrieves the request token and sets it in the unity web request's header
///
/// The web request to fill
/// Callback when the token is set
static void PrepareRequestToken(UnityWebRequest request, Action onRequestReady)
{
request.downloadHandler = new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json");
JwtToken.GetTokenAsync(token =>
{
request.SetRequestHeader("Authorization", $"Bearer {token}");
onRequestReady?.Invoke();
});
}
///
/// Returns an array of available Ad Units asynchronously
///
/// Called upon completion, with an array of ad units or null if we failed.
internal static void GetAdUnitsAsync(Action callback)
{
IsProjectReadyForConfigurationRequests(ready =>
{
if (ready)
{
var orgId = Core.Editor.OrganizationHandler.OrganizationProvider.Organization.Key;
var dashboardUri = new Uri(string.Format(k_AdUnitsListUrl, orgId, CloudProjectSettings.projectId));
var dashboardRequest = new UnityWebRequest(dashboardUri, UnityWebRequest.kHttpVerbGET);
PrepareRequestToken(dashboardRequest, () =>
{
var requestAsyncOperation = dashboardRequest.SendWebRequest();
requestAsyncOperation.completed += _ => OnAdUnitsInfoReady(dashboardRequest, callback);
});
}
else
{
MediationLogger.LogWarning(k_ErrorProjectNotConfigured);
callback?.Invoke(null);
return;
}
});
}
internal static void OnAdUnitsInfoReady(UnityWebRequest request, Action callback)
{
#if UNITY_2020_1_OR_NEWER
var isSuccess = request.result == UnityWebRequest.Result.Success;
#else
var isSuccess = !request.isNetworkError && !request.isHttpError;
#endif
if (!isSuccess)
{
MediationLogger.LogWarning($"{k_ErrorFailedToRetrieveAdUnits} {request.error}");
callback?.Invoke(null);
return;
}
var adUnitInfos = JsonUtilityExtension.FromJsonArray(request.downloadHandler.text);
callback?.Invoke(adUnitInfos);
}
///
/// Returns the game Id, asynchronously
///
/// Called upon completion, with a dictionary of platform:gameId or null if we failed
internal static void GetGameIdAsync(Action> callback)
{
IsProjectReadyForConfigurationRequests(ready =>
{
if (ready)
{
var orgId = Core.Editor.OrganizationHandler.OrganizationProvider.Organization.Key;
var dashboardUri = new Uri(string.Format(k_GameIdUrl, orgId, CloudProjectSettings.projectId, k_AdNetworkUnity));
var dashboardRequest = new UnityWebRequest(dashboardUri, UnityWebRequest.kHttpVerbGET);
PrepareRequestToken(dashboardRequest, () =>
{
var requestAsyncOperation = dashboardRequest.SendWebRequest();
requestAsyncOperation.completed += _ => OnGameIdReady(dashboardRequest, callback);
});
}
else
{
MediationLogger.LogWarning(k_ErrorProjectNotConfigured);
callback?.Invoke(null);
return;
}
});
}
internal static void GetGameIdAsyncOrWait(Action> callback)
{
if (!IsCloudProjectSettingsValid())
{
s_NoTimeoutPoller.AddCallback((success) => GetGameIdAsync(callback));
}
else
{
GetGameIdAsync(callback);
}
}
static void OnGameIdReady(UnityWebRequest request, Action> callback)
{
#if UNITY_2020_1_OR_NEWER
var isSuccess = request.result == UnityWebRequest.Result.Success;
#else
var isSuccess = !request.isNetworkError && !request.isHttpError;
#endif
if (!isSuccess)
{
//Here a 404 means the ad network is not associated with a waterfall, so the id is not available.
if (request.responseCode != 404)
{
MediationLogger.LogWarning($"{k_ErrorFailedToRetrieveGameId} {request.error}");
}
callback?.Invoke(null);
return;
}
var projectInfo = JsonUtilityExtension.FromJsonArray(request.downloadHandler.text);
var gameIdPerPlatform = new Dictionary();
foreach (var project in projectInfo)
{
gameIdPerPlatform.Add(project.Platform, project.Params.GameId);
}
callback?.Invoke(gameIdPerPlatform);
}
///
/// Returns the list of configured and enabled ad networks, asynchronously
///
/// Called upon completion, with an array of ad networks or null if we failed
internal static void GetConfiguredAdNetworksAsync(Action> callback)
{
IsProjectReadyForConfigurationRequests(ready =>
{
if (ready)
{
var orgId = Core.Editor.OrganizationHandler.OrganizationProvider.Organization.Key;
var dashboardUri = new Uri(string.Format(k_AdNetworksListUrl, orgId));
var dashboardRequest = new UnityWebRequest(dashboardUri, UnityWebRequest.kHttpVerbGET);
PrepareRequestToken(dashboardRequest, () =>
{
var requestAsyncOperation = dashboardRequest.SendWebRequest();
requestAsyncOperation.completed += _ => OnConfiguredAdNetworksReady(dashboardRequest, callback);
});
}
else
{
MediationLogger.LogWarning(k_ErrorProjectNotConfigured);
callback?.Invoke(null);
}
});
}
internal static void GetConfiguredAdNetworksAsyncOrWait(Action> callback)
{
if (!IsCloudProjectSettingsValid())
{
s_NoTimeoutPoller.AddCallback((success) => GetConfiguredAdNetworksAsync(callback));
}
else
{
GetConfiguredAdNetworksAsync(callback);
}
}
static void OnConfiguredAdNetworksReady(UnityWebRequest request, Action> callback)
{
#if UNITY_2020_1_OR_NEWER
var isSuccess = request.result == UnityWebRequest.Result.Success;
#else
var isSuccess = !request.isNetworkError && !request.isHttpError;
#endif
if (!isSuccess)
{
MediationLogger.LogWarning($"{k_ErrorFailedToRetrieveAdNetworks} {request.error}");
callback?.Invoke(null);
return;
}
AdNetworksInfoJson[] projectInfo = JsonUtilityExtension.FromJsonArray(request.downloadHandler.text);
var activeAdNetworks = new List();
foreach (var network in projectInfo)
{
if (!network.IsDisabled)
{
activeAdNetworks.Add(network.name);
}
}
callback?.Invoke(activeAdNetworks);
}
[System.Serializable]
internal class OrgInfoJson
{
public string org_foreign_key;
public string BackendId => org_foreign_key;
}
[System.Serializable]
internal class AdNetworksInfoJson
{
public string coreOrganizationId;
public string CoreOrganizationId => coreOrganizationId;
public string name;
public string Name => name;
public bool isDisabled;
public bool IsDisabled => isDisabled;
public bool? isValidated;
public bool? IsValidated => isValidated;
public string validatedAt;
public string ValidatedAt => validatedAt;
}
[System.Serializable]
internal class ProjectInfoJson
{
[System.Serializable]
public class ProjectInfoParamsJson
{
public string gameId;
public string GameId => gameId;
}
public string CoreOrganizationId { get; protected set; }
public string appId;
public string AppId => appId;
public string projectId;
public string ProjectId => projectId;
public string adNetwork;
public string AdNetwork => adNetwork;
public string platform;
public string Platform => platform;
public ProjectInfoParamsJson parameters;
public ProjectInfoParamsJson Params => parameters;
}
[System.Serializable]
internal class AdUnitInfoJson
{
public string adUnitId;
public string AdUnitId => adUnitId;
public string coreOrganization;
public string Organization => coreOrganization;
public string adFormat;
public string AdFormat => adFormat;
public string platform;
public string Platform => platform;
public string appId;
public string AppId => appId;
public string name;
public string Name => name;
public bool isArchived;
public bool IsArchived => isArchived;
}
}
}