Scripts/Editor/Data/Configuration/WitConfigurationUtility.cs (505 lines of code) (raw):

/* * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the license found in the * LICENSE file in the root directory of this source tree. */ #if UNITY_EDITOR //#define VERBOSE_LOG #endif using System; using System.Collections.Generic; using Facebook.WitAi.Data.Entities; using Facebook.WitAi.Data.Intents; using Facebook.WitAi.Data.Traits; using Facebook.WitAi.Lib; using Facebook.WitAi.Configuration; using UnityEditor; using UnityEditor.Callbacks; using UnityEngine; namespace Facebook.WitAi.Data.Configuration { public static class WitConfigurationUtility { #region ACCESS // Return wit configs public static WitConfiguration[] WitConfigs { get { // Reload if not setup if (_witConfigs == null) { ReloadConfigurationData(); } // Force reload if (_needsConfigReload) { ReloadConfigurationData(); } // Return config data return _witConfigs; } } // Wit configuration assets private static WitConfiguration[] _witConfigs = null; // Wit configuration asset names public static string[] WitConfigNames => _witConfigNames; private static string[] _witConfigNames = Array.Empty<string>(); // Config reload private static bool _needsConfigReload = false; // Has configuration public static bool HasValidCustomConfig() { // Find a valid custom configuration return Array.Exists(WitConfigs, (c) => !c.isDemoOnly); } // Enable config reload on next access public static void NeedsConfigReload() { _needsConfigReload = true; } // Refresh configuration asset list public static void ReloadConfigurationData() { // Reloaded _needsConfigReload = false; // Find all Wit Configurations List<WitConfiguration> found = new List<WitConfiguration>(); string[] guids = AssetDatabase.FindAssets("t:WitConfiguration"); foreach (var guid in guids) { string path = AssetDatabase.GUIDToAssetPath(guid); WitConfiguration config = AssetDatabase.LoadAssetAtPath<WitConfiguration>(path); if (!config.isDemoOnly) { found.Add(config); } } // Store wit configuration data _witConfigs = found.ToArray(); // Obtain all names _witConfigNames = new string[_witConfigs.Length]; for (int i = 0; i < _witConfigs.Length; i++) { _witConfigNames[i] = _witConfigs[i].name; } } // Get configuration index public static int GetConfigurationIndex(WitConfiguration configuration) { // Search through configs return Array.FindIndex(WitConfigs, (checkConfig) => checkConfig == configuration ); } // Get configuration index public static int GetConfigurationIndex(string configurationName) { // Search through configs return Array.FindIndex(WitConfigs, (checkConfig) => string.Equals(checkConfig.name, configurationName)); } // Get application id public static string GetAppID(WitConfiguration configuration) { if (configuration != null && configuration.application != null) { return configuration.application.id; } return string.Empty; } #endregion #region MANAGEMENT // Create configuration for token with blank configuration public static int CreateConfiguration(string serverToken) { // Generate blank asset WitConfiguration configurationAsset = ScriptableObject.CreateInstance<WitConfiguration>(); configurationAsset.name = WitTexts.Texts.ConfigurationFileNameLabel; configurationAsset.clientAccessToken = string.Empty; // Create int index = SaveConfiguration(serverToken, configurationAsset); if (index == -1) { MonoBehaviour.DestroyImmediate(configurationAsset); } // Return new index return index; } // Save configuration after determining path public static int SaveConfiguration(string serverToken, WitConfiguration configurationAsset) { // Determine root directory with selection if possible string rootDirectory = Application.dataPath; if (Selection.activeObject) { // Get asset path string selectedPath = AssetDatabase.GetAssetPath(Selection.activeObject); // Only allow if in assets if (selectedPath.StartsWith("Assets")) { if (AssetDatabase.IsValidFolder(selectedPath)) { rootDirectory = selectedPath; } else if (!string.IsNullOrEmpty(selectedPath)) { rootDirectory = new System.IO.FileInfo(selectedPath).DirectoryName; } } } // Determine save path string savePath = EditorUtility.SaveFilePanel(WitTexts.Texts.ConfigurationFileManagerLabel, rootDirectory, WitTexts.Texts.ConfigurationFileNameLabel, "asset"); return SaveConfiguration(savePath, serverToken, configurationAsset); } // Save configuration to selected location public static int SaveConfiguration(string savePath, string serverToken, WitConfiguration configurationAsset) { // Ensure valid save path if (string.IsNullOrEmpty(savePath)) { return -1; } // Must be in assets string unityPath = savePath.Replace("\\", "/"); if (!unityPath.StartsWith(Application.dataPath)) { Debug.LogError($"Configuration Utility - Cannot Create Configuration Outside of Assets Directory\nPath: {unityPath}"); return -1; } // Determine local unity path unityPath = unityPath.Replace(Application.dataPath, "Assets"); AssetDatabase.CreateAsset(configurationAsset, unityPath); AssetDatabase.SaveAssets(); // Refresh configurations ReloadConfigurationData(); // Get new index following reload string name = System.IO.Path.GetFileNameWithoutExtension(unityPath); int index = GetConfigurationIndex(name); _witConfigs[index].SetServerToken(serverToken); // Return index return index; } #endregion #region TOKENS // Token valid check public static bool IsServerTokenValid(string serverToken) { return !string.IsNullOrEmpty(serverToken) && WitAuthUtility.IsServerTokenValid(serverToken); } // Token valid check public static bool IsClientTokenValid(string clientToken) { return !string.IsNullOrEmpty(clientToken) && clientToken.Length == 32; } // Sets server token for all configurations if possible public static void SetServerToken(string serverToken, Action<string> onSetComplete = null) { // Invalid token if (!IsServerTokenValid(serverToken)) { SetServerTokenComplete(string.Empty, "", onSetComplete); return; } // Perform a list app request to get app for token var listRequest = WitRequestFactory.ListAppsRequest(serverToken, 10000); PerformRequest(listRequest, (r, o) => ApplyAllApplicationData(serverToken, r, o), (error) => { SetServerTokenComplete(serverToken, error, onSetComplete); }); } // Set server token complete private static void SetServerTokenComplete(string serverToken, string error, Action<string> onSetComplete) { // Failed if (!string.IsNullOrEmpty(error)) { error = $"Set Server Token Failed\n{error}"; Log(error, true); WitAuthUtility.ServerToken = ""; } // Success else { // Log Success Log("Set Server Token Success", false); // Apply token WitAuthUtility.ServerToken = serverToken; // Refresh configurations ReloadConfigurationData(); } // On complete onSetComplete?.Invoke(error); } // Sets server token for specified configuration by updating it's application data public static void SetServerToken(this WitConfiguration configuration, string serverToken, Action<string> onSetComplete = null) { // Invalid if (!IsServerTokenValid(serverToken)) { SetConfigServerTokenComplete(configuration, serverToken, "Invalid Token", onSetComplete); return; } // Refresh app data SetApplicationData(configuration, serverToken, onSetComplete); } // Refresh client data private static void SetApplicationData(WitConfiguration configuration, string serverToken, Action<string> onSetComplete) { // Already set in app server data string appID = GetAppID(configuration); if (!string.IsNullOrEmpty(appID)) { string curToken = WitAuthUtility.GetAppServerToken(appID); if (string.Equals(curToken, serverToken)) { SetClientData(configuration, serverToken, onSetComplete); return; } } // Perform a list app request to get app for token var listRequest = WitRequestFactory.ListAppsRequest(serverToken, 10000); PerformConfigRequest(configuration, listRequest, ApplyApplicationData, (error) => { // Failed if (!string.IsNullOrEmpty(error)) { SetConfigServerTokenComplete(configuration, serverToken, error, onSetComplete); } // Find client token else { SetClientData(configuration, serverToken, onSetComplete); } }); } // Refresh client data private static void SetClientData(WitConfiguration configuration, string serverToken, Action<string> onSetComplete) { // Invalid app ID string appID = GetAppID(configuration); if (string.IsNullOrEmpty(appID)) { SetConfigServerTokenComplete(configuration, serverToken, "Invalid App ID", onSetComplete); return; } // Set server token WitAuthUtility.SetAppServerToken(appID, serverToken); // Clear client token ApplyClientToken(configuration, string.Empty, null); // Find client id PerformConfigRequest(configuration, configuration.GetClientToken(appID), ApplyClientToken, (error) => { SetConfigServerTokenComplete(configuration, serverToken, error, onSetComplete); }); } // Complete private static void SetConfigServerTokenComplete(WitConfiguration configuration, string serverToken, string error, Action<string> onSetComplete) { // Failed if (!string.IsNullOrEmpty(error)) { error = "Set Configuration Server Token Failed\n" + error; Log(error, true); } // Success else { // Log success Log("Set Configuration Server Token Success", false); // Refresh data configuration.RefreshData(onSetComplete); } // On complete onSetComplete?.Invoke(error); } #endregion #region REFRESH // Refresh if possible & return true if still refreshing private static List<string> refreshAppIDs = new List<string>(); // Check if refreshing private static bool IsRefreshing(string appID) { return !string.IsNullOrEmpty(appID) && refreshAppIDs.Contains(appID); } // Check if refreshing public static bool IsRefreshingData(this WitConfiguration configuration) { string appID = GetAppID(configuration); return IsRefreshing(appID); } // Refreshes configuration data public static void RefreshData(this WitConfiguration configuration, Action<string> onRefreshComplete = null) { // Get refresh id string appID = GetAppID(configuration); if (string.IsNullOrEmpty(appID)) { RefreshDataComplete(configuration, "Cannot refresh without application data", onRefreshComplete); return; } if (Application.isPlaying) { RefreshDataComplete(configuration, "Cannot refresh while playing", onRefreshComplete); return; } if (IsRefreshing(appID)) { RefreshDataComplete(configuration, "Already Refreshing", onRefreshComplete); return; } if (!IsClientTokenValid(configuration.clientAccessToken)) { RefreshDataComplete(configuration, "Invalid client token set", onRefreshComplete); return; } // Begin refresh refreshAppIDs.Add(appID); // Refresh application data configuration.application.witConfiguration = configuration; configuration.application.UpdateData(() => { if (configuration != null) { EditorUtility.SetDirty(configuration); RefreshIntentsData(configuration, onRefreshComplete); } }); } // Refresh intents data private static void RefreshIntentsData(WitConfiguration configuration, Action<string> onRefreshComplete) { PerformConfigRequest(configuration, configuration.ListIntentsRequest(), ApplyIntentList, (error) => { if (!string.IsNullOrEmpty(error)) { RefreshDataComplete(configuration, error, onRefreshComplete); } else { RefreshEntitiesData(configuration, onRefreshComplete); } }); } // Refresh entities data private static void RefreshEntitiesData(WitConfiguration configuration, Action<string> onRefreshComplete) { PerformConfigRequest(configuration, configuration.ListEntitiesRequest(), ApplyEntityList, (error) => { if (!string.IsNullOrEmpty(error)) { RefreshDataComplete(configuration, error, onRefreshComplete); } else { RefreshTraitsData(configuration, onRefreshComplete); } }); } // Refresh traits data private static void RefreshTraitsData(WitConfiguration configuration, Action<string> onRefreshComplete) { PerformConfigRequest(configuration, configuration.ListTraitsRequest(), ApplyTraitList, (error) => { RefreshDataComplete(configuration, error, onRefreshComplete); }); } // Refresh data complete private static void RefreshDataComplete(WitConfiguration configuration, string error, Action<string> onRefreshComplete) { // Get refresh id string appID = GetAppID(configuration); if (IsRefreshing(appID)) { refreshAppIDs.Remove(appID); } // Failed if (!string.IsNullOrEmpty(error)) { error = $"Refresh Configuration Failed\n{error}"; Log(error, true); } // Success else { Log("Refresh Configuration Success", false); } // Invoke complete onRefreshComplete?.Invoke(error); } #endregion #region APPLICATION // Perform a configuration wit request and then apply configuration data private static void PerformConfigRequest(WitConfiguration configuration, WitRequest request, Action<WitConfiguration, WitResponseNode, Action<string>> onApply, Action<string> onComplete) { PerformRequest(request, (response, onRequestComplete) => { onApply(configuration, response, onRequestComplete); }, onComplete); } // Perform a wit request and then apply data private static void PerformRequest(WitRequest request, Action<WitResponseNode, Action<string>> onApply, Action<string> onComplete) { // Add response delegate request.onResponse = (response) => { // Get status int status = response.StatusCode; // Failed if (status != 200) { onComplete($"Request Failed [{status}]: {response.StatusDescription}\nPath: {request}"); } // Success else { // Apply onApply(response.ResponseData, (error) => { // Apply failed if (!string.IsNullOrEmpty(error)) { onComplete?.Invoke($"Request Set Failed: {status}\nPath: {request}\nError: {error}"); } // Complete else { Log($"Request Success\nType: {request}", false); onComplete?.Invoke(""); } }); } }; // Perform Log($"Request Begin\nType: {request}", false); request.Request(); } // Apply all application data private static void ApplyAllApplicationData(string serverToken, WitResponseNode witResponse, Action<string> onComplete) { var applications = witResponse.AsArray; for (int i = 0; i < applications.Count; i++) { // Get application var application = WitApplication.FromJson(applications[i]); string appID = application?.id; // Apply app server token if applicable if (applications[i]["is_app_for_token"].AsBool) { WitAuthUtility.SetAppServerToken(appID, serverToken); } // Apply to configuration int witConfigIndex = Array.FindIndex(WitConfigs, (configuration) => string.Equals(appID, configuration?.application?.id)); if (witConfigIndex != -1) { WitConfiguration configuration = _witConfigs[witConfigIndex]; configuration.application = application; EditorUtility.SetDirty(configuration); configuration.RefreshData(); } } onComplete(""); } // Apply application data private static void ApplyApplicationData(WitConfiguration configuration, WitResponseNode witResponse, Action<string> onComplete) { var applications = witResponse.AsArray; for (int i = 0; i < applications.Count; i++) { if (applications[i]["is_app_for_token"].AsBool) { if (configuration.application == null) { configuration.application = WitApplication.FromJson(applications[i]); } else { configuration.application.UpdateData(applications[i]); } configuration.application.witConfiguration = configuration; EditorUtility.SetDirty(configuration); onComplete?.Invoke(""); return; } } onComplete?.Invoke("No applicable configuration application found"); } // Apply client id private static void ApplyClientToken(WitConfiguration configuration, WitResponseNode witResponse, Action<string> onComplete) { var token = witResponse?["client_token"]; if (!string.IsNullOrEmpty(token)) { configuration.clientAccessToken = token; EditorUtility.SetDirty(configuration); } onComplete?.Invoke(""); } // Apply intents private static void ApplyIntentList(WitConfiguration configuration, WitResponseNode witResponse, Action<string> onComplete) { // Generate intent list var intentList = witResponse.AsArray; var n = intentList.Count; configuration.intents = new WitIntent[n]; for (int i = 0; i < n; i++) { var intent = WitIntent.FromJson(intentList[i]); intent.witConfiguration = configuration; configuration.intents[i] = intent; } EditorUtility.SetDirty(configuration); // Update intents UpdateConfigItem(0, configuration.intents, configuration, onComplete); } // Apply entities private static void ApplyEntityList(WitConfiguration configuration, WitResponseNode witResponse, Action<string> onComplete) { // Generate entities list var entityList = witResponse.AsArray; var n = entityList.Count; configuration.entities = new WitEntity[n]; for (int i = 0; i < n; i++) { var entity = WitEntity.FromJson(entityList[i]); entity.witConfiguration = configuration; configuration.entities[i] = entity; } EditorUtility.SetDirty(configuration); // Update entities UpdateConfigItem(0, configuration.entities, configuration, onComplete); } // Apply traits private static void ApplyTraitList(WitConfiguration configuration, WitResponseNode witResponse, Action<string> onComplete) { // Generate traits list var traitList = witResponse.AsArray; var n = traitList.Count; configuration.traits = new WitTrait[n]; for (int i = 0; i < n; i++) { var trait = WitTrait.FromJson(traitList[i]); trait.witConfiguration = configuration; configuration.traits[i] = trait; } EditorUtility.SetDirty(configuration); // Update traits UpdateConfigItem(0, configuration.traits, configuration, onComplete); } // Update all private static void UpdateConfigItem(int index, WitConfigurationData[] items, WitConfiguration configuration, Action<string> onComplete) { // Complete if (index < 0 || index >= items.Length) { onComplete?.Invoke(""); return; } // Update item WitConfigurationData item = items[index]; item.UpdateData(() => { Log($"{item.GetType()} {index} Updated", false); EditorUtility.SetDirty(configuration); UpdateConfigItem(index + 1, items, configuration, onComplete); }); } // Log private static void Log(string comment, bool error) { #if VERBOSE_LOG string l = "Wit Configuration Utility - " + comment; if (error) { Debug.LogError(l); } else { Debug.Log(l); } #endif } #endregion } }