diff --git a/README.md b/README.md index 153765b..86d0b90 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,37 @@ # CiscoSpark-UnitySDK -Native Cisco Spark SDK for Unity 5.4 +Native Cisco Spark SDK for Unity 5.4+ -Very much **unoffical**, **pre-release** and under **active development**! Pull requests most welcome. +You can follow the progress + development of this here: https://trello.com/b/BvpwAZYd/unity-spark-sdk -This library takes advantage of the new `UnityEngine.Networking.UnityWebRequest` functionality introduced in Unity 5.4, as well as running all methods via Coroutines and returning results via `Actions`, in order to be non-blocking. +Basic `TODO` list: +- Better Testing +- Inheritance rewrite +- Documentation -You can follow the progress + development of this here: https://trello.com/b/BvpwAZYd/unity-spark-sdk -Please note: I'm coming from a hacky python background, and this is the first self-contained public library I've ever attempted to build, so I'm very much learning as I go. The more I learn, the more mistakes I realise I've made, so please point things out that I should change! +## About +This library takes advantage of the new `UnityEngine.Networking.UnityWebRequest` functionality introduced in Unity 5.4, as well as running all methods via Coroutines and returning results and errors using `System.Action`, in order to be non-blocking. I took inspiration from the NodeJS callback style using error, response. + +The SDK gives the following for each object: + +- Ability to create, commit and delete + - Committing a `Id=null` local object will create it on Spark + - If an ID is set it will update +- Ability to List all objects from Spark matching a given query. +- Ability to retrieve a specific instance from Spark. +- A `SparkMessage` will be returned via the `error` callback if Spark cannot complete a request. + +Please see the Spark Developer documentation at http://developer.ciscospark.com for details about specific requests. This SDK will always aim for parity with the APIs documented there. ## Setup -1. Import the scripts or UnityPackage +1. Import the scripts or UnityPackage. 2. Place the `Request` script on any GameObject. -3. Set the `BaseUrl` and `AuthenticationString` variables in the Inspector. - - `BaseUrl` is currently: https://api.ciscospark.com/v1 +3. Set the `AuthenticationString` variable in the Inspector. + - `BaseUrl` defaults to: https://api.ciscospark.com/v1 - Your `AuthenticationToken` or your bot's token can be found at http://developer.ciscospark.com - ## Examples -Here is an example of sending a message to a given room from Unity: +Here is an example of sending a message to a given room from Unity (without knowing the `RoomId` beforehand): ```c# using UnityEngine; @@ -26,15 +39,20 @@ using Cisco.Spark; public class Spark : MonoBehaviour { void Start() { - StartCoroutine (Room.ListRooms (rooms => { - foreach (Room room in rooms) { - if (room.Title == "Unity Test Room") { - Message testMessage = new Message(); + StartCoroutine (Room.ListRooms (error => { + if (error != null) { + Debug.LogError(error.Message); + foreach (var sparkError in error.Errors) { + Debug.LogError(sparkError.Description); + } + } + }, rooms => { + foreach (var room in rooms) { + if (room.Title == "Test Room") { + var testMessage = new Message (); testMessage.RoomId = room.Id; testMessage.Markdown = "This message came from **unity**"; - StartCoroutine (testMessage.Commit(message => { - Debug.Log("Created message: " + message.Id); - })); + StartCoroutine (testMessage.Commit (callback => Debug.Log ("Created message: " + testMessage.Id))); } } })); @@ -45,25 +63,39 @@ public class Spark : MonoBehaviour { Here is an example of downloading all files from a given room, and placing them onto cubes: ```c# -StartCoroutine (Message.ListMessages (, messages => { - foreach (Message message in messages) { - if (message.Files != null) { - foreach (SparkFile file in message.Files) { - StartCoroutine(file.Download (callback => { - if (file.returnType == typeof(UnityEngine.Texture2D)) { - GameObject test = GameObject.CreatePrimitive (PrimitiveType.Cube); - test.GetComponent().material.mainTexture = callback as Texture2D; - } - })); +using UnityEngine; +using Cisco.Spark; + +public class Spark : MonoBehaviour { + void Start() { + StartCoroutine (Message.ListMessages ("Y2lzY29zcGFyazovL3VzL1JPT00vMzFhOTVkYTAtZjgwYi0xMWU1LWIyMjgtNTk1Mjc3YjMwNDli", + error => { + if (error != null) { + Debug.LogError ("Failed: " + error.Message); + } + }, + messages => { + foreach (var message in messages) { + if (message.Files != null) { + foreach (var file in message.Files) { + StartCoroutine(file.Download (callback => { + if (file.ReturnType == typeof(Texture2D)) { + var test = GameObject.CreatePrimitive (PrimitiveType.Cube); + test.name = file.Filename; + test.transform.position = new Vector3(Random.Range (0, 25), Random.Range (0, 25), Random.Range (0, 25)); + test.GetComponent().material.mainTexture = callback as Texture2D; + } + })); + } + } + } } - } + )); } -})); +} ``` ## Tests -*Tests are pretty lackluster at the moment. I'll work on getting them up to a proper standard once I hit parity with the Web.* - -Unfortunately, it is not possible to run tests for the SDK using the builtin Unity Test Tools, due to a lack of support for running Asynchronous operations. As a result, I have created some `MonoBehaviour` scripts that will run the tests that can be found in `Assets/Tests` in order to simulate the environment they will be run in. To run them, just attach any of the `Test*` scripts and `Request` to a `GameObject`, and the results will be outputted to the console. +Unfortunately, there is a lack of support for running Asynchronous operations in Unity Tests. Instead, there are `MonoBehaviour` test scripts included that will run chains of requests. To run them, just attach any of the `Test*` scripts and `Request` to a `GameObject`, and the results will be outputted to the console. **Note: This will create/edit/destroy real test rooms/memberships/etc on the given Spark account, but they will clean up after themselves if possible.** diff --git a/Spark SDK/Assets/Cisco/Spark SDK/TeamMembership.cs b/Spark SDK/Assets/Cisco/Spark SDK/TeamMembership.cs deleted file mode 100644 index 27dbebe..0000000 --- a/Spark SDK/Assets/Cisco/Spark SDK/TeamMembership.cs +++ /dev/null @@ -1,14 +0,0 @@ -using UnityEngine; -using System; - -namespace Cisco.Spark { - public class TeamMembership : ScriptableObject { - string Id { get; set;} - string TeamId { get; set;} - string PersonId { get; set;} - string PersonDisplayName { get; set;} - string PersonEmail { get; set;} - bool IsModerator { get; set;} - DateTime Created { get; set;} - } -} \ No newline at end of file diff --git a/Spark SDK/Assets/Cisco/Spark SDK/Webhook.cs b/Spark SDK/Assets/Cisco/Spark SDK/Webhook.cs deleted file mode 100644 index 56551d3..0000000 --- a/Spark SDK/Assets/Cisco/Spark SDK/Webhook.cs +++ /dev/null @@ -1,14 +0,0 @@ -using UnityEngine; -using System; - -namespace Cisco.Spark { - public class Webhook : ScriptableObject { - string id { get; set;} - string webhookName { get; set;} - string resource { get; set;} - string eventType { get; set;} - string filter { get; set;} - Uri targetUrl { get; set;} - DateTime created { get; set;} - } -} \ No newline at end of file diff --git a/Spark SDK/Assets/Tests/TestRoom.cs b/Spark SDK/Assets/Tests/TestRoom.cs deleted file mode 100644 index da00712..0000000 --- a/Spark SDK/Assets/Tests/TestRoom.cs +++ /dev/null @@ -1,76 +0,0 @@ -using UnityEngine; -using System.Threading; -using Cisco.Spark; - -/// -/// Class to test the functionality. -/// -public class TestRoom : MonoBehaviour { - - void Start () { - int errorCount = 0; - Debug.Log ("Running Room tests"); - - // List Rooms - StartCoroutine (Room.ListRooms (rooms => { - int startRoomCount = rooms.Count; - - // Create New Room - Room testRoom = new Room("Test Room (CiscoSpark-UnitySDK)", null); - StartCoroutine(testRoom.Commit (room => { - testRoom = room; - if (testRoom.Title != "Test Room (CiscoSpark-UnitySDK)") { - Debug.LogError("Create Room Failed!"); - errorCount++; - } else { - Debug.Log("Create Room Passed!"); - startRoomCount++; - } - - // Edit Room - testRoom.Title = "Updated Test Room (CiscoSpark-UnitySDK)"; - StartCoroutine (testRoom.Commit(updatedRoom => { - testRoom = updatedRoom; - if (testRoom.Title != "Updated Test Room (CiscoSpark-UnitySDK)") { - Debug.LogError ("Update Room Failed!"); - errorCount++; - } else { - Debug.Log("Update Room Passed!"); - } - - // Get Room Details - StartCoroutine (Room.GetRoomDetails (testRoom.Id, retrivedRoom => { - testRoom = retrivedRoom; - if (testRoom.Title != "Updated Test Room (CiscoSpark-UnitySDK)") { - Debug.LogError ("GetRoomDetails Failed!"); - } else { - Debug.Log("GetRoomDetails Passed!"); - } - - // Delete Room - StartCoroutine (testRoom.Delete ()); - Thread.Sleep (1000); // Wait for Delete to finish - startRoomCount--; - StartCoroutine (Room.ListRooms (postDeleteRooms => { - if (startRoomCount != postDeleteRooms.Count) { - Debug.LogError("Delete Room Failed!"); - errorCount++; - } else { - Debug.Log("Delete Room Passed!"); - Debug.Log("ListRooms Passed!"); - } - - // Finish and Report - Debug.Log ("Finished Running Room Tests"); - if (errorCount == 0) { - Debug.Log("All tests passed!"); - } else { - Debug.LogError (errorCount + " tests failed!"); - } - })); - })); - })); - })); - })); - } -} diff --git a/Spark SDK/Assets/Tests/TestTeam.cs b/Spark SDK/Assets/Tests/TestTeam.cs deleted file mode 100644 index d7fbddc..0000000 --- a/Spark SDK/Assets/Tests/TestTeam.cs +++ /dev/null @@ -1,63 +0,0 @@ -using UnityEngine; -using Cisco.Spark; - -public class TestTeam : MonoBehaviour { - void Start () { - var errorCount = 0; - // List Teams - StartCoroutine (Team.ListTeams (teams => { - if (teams.Count > 0) { - Debug.Log ("ListTeams passed"); - } else { - errorCount++; - Debug.LogError ("ListTeams failed"); - } - - // Get Team Details - StartCoroutine (Team.GetTeamDetails (teams[0].Id, retrievedTeam => { - if (retrievedTeam.Name != teams[0].Name) { - errorCount++; - Debug.LogError("GetTeamDetails failed"); - } else { - Debug.Log("GetTeamDetails passed"); - } - - // Create Team - const string OriginalName = "Unity SDK Test Team"; - Team t = new Team(OriginalName); - StartCoroutine (t.Commit (createdTeam => { - t = createdTeam; - if (t.Id == null) { - errorCount++; - Debug.LogError ("Create Team failed"); - } else { - Debug.Log("Create Team passed"); - } - - // Update Team - const string NewName = OriginalName + "RENAMED"; - t.Name = NewName; - StartCoroutine (t.Commit (updatedTeam => { - t = updatedTeam; - if (t.Name != NewName) { - errorCount++; - Debug.LogError ("Update Team failed"); - } else { - Debug.Log("Update Team passed"); - } - - // Delete Team - StartCoroutine (t.Delete ()); - - // Error Report - if (errorCount > 0) { - Debug.Log(errorCount + " Team tests failed"); - } else { - Debug.Log("All Team tests passed"); - } - })); - })); - })); - })); - } -} diff --git a/Spark SDK/.gitignore b/SparkUnity/.gitignore similarity index 85% rename from Spark SDK/.gitignore rename to SparkUnity/.gitignore index 5aafcbb..8aa2d92 100644 --- a/Spark SDK/.gitignore +++ b/SparkUnity/.gitignore @@ -4,6 +4,9 @@ /[Bb]uild/ /[Bb]uilds/ /Assets/AssetStoreTools* +/Assets/DevelopmentTools* +/Assets/Plugins* +/Assets/UnityVS* # Autogenerated VS/MD solution and project files ExportedObj/ diff --git a/Spark SDK/Assets/Cisco.meta b/SparkUnity/Assets/Cisco.meta similarity index 100% rename from Spark SDK/Assets/Cisco.meta rename to SparkUnity/Assets/Cisco.meta diff --git a/Spark SDK/Assets/Cisco/Spark SDK.meta b/SparkUnity/Assets/Cisco/Spark.meta similarity index 100% rename from Spark SDK/Assets/Cisco/Spark SDK.meta rename to SparkUnity/Assets/Cisco/Spark.meta diff --git a/Spark SDK/Assets/Cisco/Spark SDK/Membership.cs b/SparkUnity/Assets/Cisco/Spark/Membership.cs similarity index 50% rename from Spark SDK/Assets/Cisco/Spark SDK/Membership.cs rename to SparkUnity/Assets/Cisco/Spark/Membership.cs index 59f7f5b..0620949 100644 --- a/Spark SDK/Assets/Cisco/Spark SDK/Membership.cs +++ b/SparkUnity/Assets/Cisco/Spark/Membership.cs @@ -14,33 +14,20 @@ public class Membership { public string PersonEmail { get; set;} public bool IsModerator { get; set;} public bool IsMonitor { get; set;} - public string Created { get; set;} + public DateTime Created { get; private set;} /// - /// Initializes a new object from an existing - /// Spark membership. + /// Initializes a new instance of the class. /// - /// Json. - public Membership(string json) { - Dictionary membershipData = Json.Deserialize (json) as Dictionary; - try { - Id = membershipData ["id"] as string; - RoomId = membershipData ["roomId"] as string; - PersonId = membershipData ["personId"] as string; - PersonEmail = membershipData ["personEmail"] as string; - PersonDisplayName = membershipData ["personDisplayName"] as string; - IsModerator = (bool) membershipData ["isModerator"]; - IsMonitor = (bool) membershipData ["isMonitor"]; - Created = membershipData ["created"] as string; - } catch (KeyNotFoundException) { - // TODO: Spark Exceptions - Debug.Log ("Couldn't parse Membership"); - } - } + public Membership() { } /// - /// Initializes a new object locally. + /// Initializes a new instance of the class. /// + /// Room identifier. + /// Person identifier. + /// Person email. + /// If set to true is moderator. public Membership(string roomId, string personId, string personEmail, bool isModerator) { RoomId = roomId; PersonId = personId; @@ -49,21 +36,35 @@ public Membership(string roomId, string personId, string personEmail, bool isMod } /// - /// Initializes a new object locally. + /// Initializes a new instance of the class. /// - public Membership() { } + /// Membership data. + Membership(Dictionary membershipData) { + try { + Id = membershipData ["id"] as string; + RoomId = membershipData ["roomId"] as string; + PersonId = membershipData ["personId"] as string; + PersonEmail = membershipData ["personEmail"] as string; + PersonDisplayName = membershipData ["personDisplayName"] as string; + IsModerator = (bool) membershipData ["isModerator"]; + IsMonitor = (bool) membershipData ["isMonitor"]; + Created = DateTime.Parse ((string) membershipData ["created"]); + } catch (KeyNotFoundException) { + Debug.Log ("Couldn't parse Membership"); + } + } /// - /// Commits the current state of the local object to Spark. - /// This will create a new if it doesn't exist. + /// Commit the specified error and result. /// - /// The created/updated from Spark - public IEnumerator Commit(Action callback) { + /// Error. + /// Result. + public IEnumerator Commit(Action error, Action result) { // Setup request from current state of Room object - Request manager = GameObject.FindObjectOfType (); + var manager = GameObject.FindObjectOfType (); // Membership Data - Dictionary data = new Dictionary (); + var data = new Dictionary (); // Create or Update? string resource; @@ -92,22 +93,45 @@ public IEnumerator Commit(Action callback) { if (www.isError) { Debug.LogError("Failed to Create Membership: " + www.error); } else { - Membership membership = new Membership (www.downloadHandler.text); - callback (membership); + // Parse Response + var membershipData = Json.Deserialize (www.downloadHandler.text) as Dictionary; + if (membershipData.ContainsKey ("message")) { + // Spark Error + error (new SparkMessage (membershipData)); + result (null); + } else { + // Create local Membership object + error(null); + result(new Membership (membershipData)); + } } } } /// - /// Delete the specified + /// Delete the specified error and result. /// - public IEnumerator Delete() { + /// Error. + /// Result. + public IEnumerator Delete(Action error, Action result) { if (Id != null) { - Request manager = GameObject.FindObjectOfType (); + var manager = GameObject.FindObjectOfType (); using (UnityWebRequest www = manager.Generate ("memberships/" + Id, UnityWebRequest.kHttpVerbDELETE)) { yield return www.Send (); if (www.isError) { + // Network Error Debug.LogError ("Failed to Delete Membership: " + www.error); + } else { + // Delete returns 204 on success + if (www.responseCode == 204) { + error (null); + result (true); + } else { + // Delete Failed + var json = Json.Deserialize (www.downloadHandler.text) as Dictionary; + error (new SparkMessage (json)); + result (false); + } } } } @@ -117,24 +141,25 @@ public IEnumerator Delete() { /// Lists the memberships. /// /// The memberships. - /// Callback. + /// Error. + /// Result. /// Room identifier. /// Person identifier. - /// Person email - /// Max number of Memberships to return - public static IEnumerator ListMemberships(Action> callback, string roomId = null, string personId = null, string personEmail = null, int max = 0) { - Request manager = GameObject.FindObjectOfType (); + /// Person email. + /// Max. + public static IEnumerator ListMemberships(Action error, Action> result, string roomId = null, string personId = null, string personEmail = null, int max = 0) { + var manager = GameObject.FindObjectOfType (); // Optional Parameters - Dictionary data = new Dictionary (); + var data = new Dictionary (); if (roomId != null) { data ["roomId"] = roomId; } if (personId != null) { - data ["personId"] = roomId; + data ["personId"] = personId; } if (personEmail != null) { - data ["personEmail"] = roomId; + data ["personEmail"] = personEmail; } if (max != 0) { data ["max"] = max.ToString (); @@ -146,36 +171,59 @@ public static IEnumerator ListMemberships(Action> callback, str // Make Request using (UnityWebRequest www = manager.Generate ("memberships?" + queryString, UnityWebRequest.kHttpVerbGET)) { yield return www.Send (); + if (www.isError) { - Debug.LogError ("Failed to List Memberships"); + // Network error + Debug.LogError("Failed to List Memberships: " + www.error); } else { - // Convert to Membership objects - List memberships = new List (); - Dictionary json = Json.Deserialize (www.downloadHandler.text) as Dictionary; - List items = json ["items"] as List; - foreach (Dictionary membership_json in items) { - string reJsoned = Json.Serialize (membership_json); - memberships.Add(new Membership (reJsoned)); + // Request succeeded, parse response + var json = Json.Deserialize (www.downloadHandler.text) as Dictionary; + + // Check for Spark side errors + if (json.ContainsKey ("message")) { + error (new SparkMessage (json)); + result (null); + } else { + // Convert to Membership objects + var memberships = new List (); + var items = json ["items"] as List; + foreach (var membership in items) { + memberships.Add (new Membership (membership as Dictionary)); + } + result (memberships); + error (null); } - callback (memberships); } } } - + /// /// Gets the membership details. /// /// The membership details. - /// The Membership. + /// Error. + /// Result. /// Membership identifier. - public static IEnumerator GetMembershipDetails(Action callback, string membershipId) { + public static IEnumerator GetMembershipDetails(string membershipId, Action error, Action result) { Request manager = GameObject.FindObjectOfType (); using (UnityWebRequest www = manager.Generate ("memberships/" + membershipId, UnityWebRequest.kHttpVerbGET)) { yield return www.Send (); + if (www.isError) { - Debug.LogError ("Failed to retrieve Membership"); + // Network error + Debug.LogError (www.error); } else { - callback(new Membership (www.downloadHandler.text)); + // Parse Response + var membershipData = Json.Deserialize (www.downloadHandler.text) as Dictionary; + if (membershipData.ContainsKey ("message")) { + // Error Callback + error (new SparkMessage (membershipData)); + result(null); + } else { + // Result callback + error (null); + result(new Membership (membershipData)); + } } } } diff --git a/Spark SDK/Assets/Cisco/Spark SDK/Membership.cs.meta b/SparkUnity/Assets/Cisco/Spark/Membership.cs.meta similarity index 100% rename from Spark SDK/Assets/Cisco/Spark SDK/Membership.cs.meta rename to SparkUnity/Assets/Cisco/Spark/Membership.cs.meta diff --git a/Spark SDK/Assets/Cisco/Spark SDK/Message.cs b/SparkUnity/Assets/Cisco/Spark/Message.cs similarity index 68% rename from Spark SDK/Assets/Cisco/Spark SDK/Message.cs rename to SparkUnity/Assets/Cisco/Spark/Message.cs index 29e64e0..1b517d6 100644 --- a/Spark SDK/Assets/Cisco/Spark SDK/Message.cs +++ b/SparkUnity/Assets/Cisco/Spark/Message.cs @@ -1,9 +1,9 @@ -using UnityEngine; -using UnityEngine.Networking; -using System; +using System; using System.Collections; using System.Collections.Generic; using MiniJSON; +using UnityEngine; +using UnityEngine.Networking; namespace Cisco.Spark { public class Message { @@ -17,24 +17,24 @@ public class Message { public List Files { get; set;} public string PersonId { get; set;} public string PersonEmail { get; set;} - public string Created { get; set;} + public DateTime Created { get; private set;} /// - /// Initializes a new instance of the class from a Spark API retrieval. + /// Initializes a new locally. Use to + /// save to the Spark service. /// - /// JSON Representation of the message - public Message(string json) { - var data = Json.Deserialize (json) as Dictionary; - - // TODO: Proper Spark Exceptions - object message; - if (data.TryGetValue ("message", out message)) { - Debug.Log (message as string); - return; - } + public Message() { } + /// + /// Initializes a new instance of the class from a Spark API retrieval. + /// + /// Message data from Spark. + Message(Dictionary data) { Id = data ["id"] as string; RoomId = data ["roomId"] as string; + PersonId = data ["personId"] as string; + PersonEmail = data ["personEmail"] as string; + Created = DateTime.Parse ((string)data ["created"]); // Handle Optionals object toPersonId; @@ -72,25 +72,15 @@ public Message(string json) { Files.Add (new SparkFile(fileId)); } } - - PersonId = data ["personId"] as string; - PersonEmail = data ["personEmail"] as string; - } - - /// - /// Initializes a new locally. Use to - /// save to the Spark service. - /// - public Message() { - } /// /// Commits the current state of the local Room object to Spark. /// This will create a new room if it doesn't exist. /// - /// The created/updated Room from Spark - public IEnumerator Commit(Action callback) { + /// The error from Spark (if any). + /// The created/updated Room from Spark. + public IEnumerator Commit(Action error, Action result) { // Setup request from current state of Room object var manager = GameObject.FindObjectOfType (); @@ -138,22 +128,41 @@ public IEnumerator Commit(Action callback) { if (www.isError) { Debug.LogError("Failed to Create Message: " + www.error); } else { - var message = new Message (www.downloadHandler.text); - callback (message); + var messageData = Json.Deserialize (www.downloadHandler.text) as Dictionary; + if (messageData.ContainsKey ("message")) { + error (new SparkMessage (messageData)); + result (null); + } else { + result(new Message (messageData)); + error (null); + } } } } - + /// - /// Delete this Message on Spark. + /// Deleted the message on Spark. /// - public IEnumerator Delete() { + /// Error. + /// Result. + public IEnumerator Delete(Action error, Action result) { if (Id != null) { var manager = GameObject.FindObjectOfType (); using (UnityWebRequest www = manager.Generate ("messages/" + Id, UnityWebRequest.kHttpVerbDELETE)) { yield return www.Send (); if (www.isError) { Debug.LogError ("Failed to Delete Message: " + www.error); + } else { + // Delete returns 204 on success + if (www.responseCode == 204) { + error (null); + result (true); + } else { + // Delete Failed + var json = Json.Deserialize (www.downloadHandler.text) as Dictionary; + error (new SparkMessage (json)); + result (false); + } } } } @@ -164,29 +173,38 @@ public IEnumerator Delete() { /// /// The Message object /// Message identifier. - /// The message object - public static IEnumerator GetMessageDetails(string messageId, Action callback) { + /// Error. + /// Result. + public static IEnumerator GetMessageDetails(string messageId, Action error, Action result) { var manager = GameObject.FindObjectOfType (); using (UnityWebRequest www = manager.Generate ("messages/" + messageId, UnityWebRequest.kHttpVerbGET)) { yield return www.Send (); if (www.isError) { Debug.LogError ("Failed to Retrieve Message: " + www.error); } else { - callback(new Message(www.downloadHandler.text)); + var messageDetails = Json.Deserialize (www.downloadHandler.text) as Dictionary; + if (messageDetails.ContainsKey ("message")) { + error (new SparkMessage (messageDetails)); + result (null); + } else { + result (new Message (messageDetails)); + error (null); + } } } } - + /// /// Lists the messages. /// /// The messages. /// Room identifier. - /// The list of Messages - /// Before - /// Before message. - /// Max number of messages to recieve - public static IEnumerator ListMessages(string roomId, Action> callback, string before = null, string beforeMessage = null, int max = 0) { + /// Error. + /// Result. + /// + /// List all messages before a specific message ID. + /// Max number of messages. + public static IEnumerator ListMessages(string roomId, Action error, Action> result, string before = null, string beforeMessage = null, int max = 0) { var manager = GameObject.FindObjectOfType (); var data = new Dictionary (); data ["roomId"] = roomId; @@ -209,17 +227,18 @@ public static IEnumerator ListMessages(string roomId, Action> call if (www.isError) { Debug.LogError ("Failed to List Messages: " + www.error); } else { - var messages = new List (); var json = Json.Deserialize (www.downloadHandler.text) as Dictionary; - try { + if (json.ContainsKey ("message")) { + error (new SparkMessage (json)); + result (null); + } else { + var messages = new List(); var items = json ["items"] as List; - foreach (var message_json in items) { - string reJsoned = Json.Serialize (message_json); - messages.Add (new Message (reJsoned)); + foreach (Dictionary message_json in items) { + messages.Add (new Message (message_json)); } - callback (messages); - } catch (KeyNotFoundException) { - Debug.LogError (www.downloadHandler.text); + result (messages); + error (null); } } } diff --git a/Spark SDK/Assets/Cisco/Spark SDK/Message.cs.meta b/SparkUnity/Assets/Cisco/Spark/Message.cs.meta similarity index 100% rename from Spark SDK/Assets/Cisco/Spark SDK/Message.cs.meta rename to SparkUnity/Assets/Cisco/Spark/Message.cs.meta diff --git a/Spark SDK/Assets/Cisco/Spark SDK/Person.cs b/SparkUnity/Assets/Cisco/Spark/Person.cs similarity index 51% rename from Spark SDK/Assets/Cisco/Spark SDK/Person.cs rename to SparkUnity/Assets/Cisco/Spark/Person.cs index aa422bb..5fd73aa 100644 --- a/Spark SDK/Assets/Cisco/Spark SDK/Person.cs +++ b/SparkUnity/Assets/Cisco/Spark/Person.cs @@ -10,7 +10,7 @@ public class Person { public string Id { get; private set;} public string DisplayName { get; set;} public string Avatar { get; set;} - public string Created { get; private set;} + public DateTime Created { get; private set;} public List Emails { get; set;} public string Status { get; private set;} @@ -18,15 +18,18 @@ public class Person { /// Initializes a new instance of the /// class from an existing Spark record /// - /// JSON from Cisco Spark - public Person(string json) { - var details = Json.Deserialize (json) as Dictionary; + /// Person data from Cisco Spark + Person(Dictionary details) { Id = (string) details ["id"]; DisplayName = (string) details ["displayName"]; Avatar = (string) details["avatar"]; - // TODO: Created as DateTime - Created = (string) details ["created"]; - Status = (string) details ["status"]; + Created = DateTime.Parse ((string) details ["created"]); + + // Status not always implemented + object status; + if (details.TryGetValue ("status", out status)) { + Status = (string) details ["status"]; + } object emails; if (details.TryGetValue ("emails", out emails)) { @@ -42,15 +45,24 @@ public Person(string json) { /// Downloads the user's avatar as a Texture /// /// Texture of an avatar + /// Error if any from Spark /// Callback. - public IEnumerator DownloadAvatar(Action callback) { + public IEnumerator DownloadAvatar(Action error, Action callback) { UnityWebRequest www = UnityWebRequest.GetTexture (Avatar); yield return www.Send (); if (www.isError) { Debug.LogError ("Failed to Download Avatar: " + www.error); } else { - Texture texture = ((DownloadHandlerTexture)www.downloadHandler).texture; - callback (texture); + var texture = ((DownloadHandlerTexture)www.downloadHandler).texture; + if (texture) { + callback (texture); + error (null); + } else { + // TODO: Check what happens on failed avatar calls + Debug.LogError ("Download avatar failed. Check implementation."); + callback (null); + error (null); + } } } @@ -58,9 +70,10 @@ public IEnumerator DownloadAvatar(Action callback) { /// Gets details of the currently authenticated user from Spark. /// /// The Person object. + /// Error. /// Callback. - static public IEnumerator GetPersonDetails(Action callback) { - yield return GetPersonDetails ("me", callback); + static public IEnumerator GetPersonDetails(Action error, Action callback) { + yield return GetPersonDetails ("me", error, callback); } /// @@ -68,17 +81,28 @@ static public IEnumerator GetPersonDetails(Action callback) { /// /// The Person object /// Person identifier. - /// Callback. - static public IEnumerator GetPersonDetails(string personId, Action callback) { - var manager = GameObject.FindObjectOfType (); - using (UnityWebRequest www = manager.Generate("people/" + personId, UnityWebRequest.kHttpVerbGET)) { + /// Error from Spark + /// Callback. + public static IEnumerator GetPersonDetails(string personId, Action error, Action result) { + Request manager = GameObject.FindObjectOfType (); + using (UnityWebRequest www = manager.Generate ("people/" + personId, UnityWebRequest.kHttpVerbGET)) { yield return www.Send (); - if (www.error == null) { - var person = new Person (www.downloadHandler.text); - callback (person); + + if (www.isError) { + // Network error + Debug.LogError (www.error); } else { - Debug.LogError ("Failed to Get Person Details: " + www.error); - callback (null); + // Parse Response + var personData = Json.Deserialize (www.downloadHandler.text) as Dictionary; + if (personData.ContainsKey ("message")) { + // Error Callback + error (new SparkMessage (personData)); + result(null); + } else { + // Result callback + error (null); + result(new Person (personData)); + } } } } @@ -87,11 +111,12 @@ static public IEnumerator GetPersonDetails(string personId, Action callb /// Lists the people matched by the query /// /// List of People - /// Callback. - /// Email to search on - /// Display name to search on - /// Max number of people to return - public static IEnumerator ListPeople(Action> callback, string email = null, string displayName = null, int max = 0) { + /// Error from Spark. + /// Callback. + /// Email to search on. + /// Display name to search on. + /// Max number of people to return. + public static IEnumerator ListPeople(Action error, Action> result, string email = null, string displayName = null, int max = 0) { if (email == null && displayName == null) { Debug.LogError ("Email or displayName should be specified."); } @@ -120,14 +145,20 @@ public static IEnumerator ListPeople(Action> callback, string email Debug.LogError ("Failed to List People"); } else { // Convert to Person objects - var people = new List (); var json = Json.Deserialize (www.downloadHandler.text) as Dictionary; - var items = json ["items"] as List; - foreach (Dictionary person_json in items) { - string reJsoned = Json.Serialize (person_json); - people.Add(new Person (reJsoned)); + if (json.ContainsKey ("message")) { + error (new SparkMessage (json)); + result (null); + } else { + // Convert to Membership objects + var people = new List (); + var items = json ["items"] as List; + foreach (var person in items) { + people.Add (new Person (person as Dictionary)); + } + result (people); + error (null); } - callback (people); } } } diff --git a/Spark SDK/Assets/Cisco/Spark SDK/Person.cs.meta b/SparkUnity/Assets/Cisco/Spark/Person.cs.meta similarity index 100% rename from Spark SDK/Assets/Cisco/Spark SDK/Person.cs.meta rename to SparkUnity/Assets/Cisco/Spark/Person.cs.meta diff --git a/Spark SDK/Assets/Cisco/Spark SDK/Plugins.meta b/SparkUnity/Assets/Cisco/Spark/Plugins.meta similarity index 100% rename from Spark SDK/Assets/Cisco/Spark SDK/Plugins.meta rename to SparkUnity/Assets/Cisco/Spark/Plugins.meta diff --git a/Spark SDK/Assets/Cisco/Spark SDK/Plugins/MiniJSON.cs b/SparkUnity/Assets/Cisco/Spark/Plugins/MiniJSON.cs similarity index 100% rename from Spark SDK/Assets/Cisco/Spark SDK/Plugins/MiniJSON.cs rename to SparkUnity/Assets/Cisco/Spark/Plugins/MiniJSON.cs diff --git a/Spark SDK/Assets/Cisco/Spark SDK/Plugins/MiniJSON.cs.meta b/SparkUnity/Assets/Cisco/Spark/Plugins/MiniJSON.cs.meta similarity index 100% rename from Spark SDK/Assets/Cisco/Spark SDK/Plugins/MiniJSON.cs.meta rename to SparkUnity/Assets/Cisco/Spark/Plugins/MiniJSON.cs.meta diff --git a/Spark SDK/Assets/Cisco/Spark SDK/Request.cs b/SparkUnity/Assets/Cisco/Spark/Request.cs similarity index 74% rename from Spark SDK/Assets/Cisco/Spark SDK/Request.cs rename to SparkUnity/Assets/Cisco/Spark/Request.cs index a9923a4..093e495 100644 --- a/Spark SDK/Assets/Cisco/Spark SDK/Request.cs +++ b/SparkUnity/Assets/Cisco/Spark/Request.cs @@ -6,12 +6,12 @@ public class Request : MonoBehaviour { public const string BaseUrl = "https://api.ciscospark.com/v1"; public string AuthenticationToken = ""; - public UnityWebRequest Generate(string Resource, string RequestType) { + public UnityWebRequest Generate(string resource, string requestType) { // Setup Headers - UnityWebRequest www = new UnityWebRequest (BaseUrl + "/" + Resource); + var www = new UnityWebRequest (BaseUrl + "/" + resource); www.SetRequestHeader ("Authorization", "Bearer " + AuthenticationToken); www.SetRequestHeader ("Content-type", "application/json; charset=utf-8"); - www.method = RequestType; + www.method = requestType; www.downloadHandler = new DownloadHandlerBuffer(); return www; } diff --git a/Spark SDK/Assets/Cisco/Spark SDK/Request.cs.meta b/SparkUnity/Assets/Cisco/Spark/Request.cs.meta similarity index 100% rename from Spark SDK/Assets/Cisco/Spark SDK/Request.cs.meta rename to SparkUnity/Assets/Cisco/Spark/Request.cs.meta diff --git a/Spark SDK/Assets/Cisco/Spark SDK/Room.cs b/SparkUnity/Assets/Cisco/Spark/Room.cs similarity index 57% rename from Spark SDK/Assets/Cisco/Spark SDK/Room.cs rename to SparkUnity/Assets/Cisco/Spark/Room.cs index 75cb346..9aa1c54 100644 --- a/Spark SDK/Assets/Cisco/Spark SDK/Room.cs +++ b/SparkUnity/Assets/Cisco/Spark/Room.cs @@ -7,25 +7,37 @@ namespace Cisco.Spark { public class Room { - public string Id { get; set; } + public string Id { get; private set; } public string Title { get; set; } public string RoomType { get; set;} public bool IsLocked { get; set; } public string TeamId { get; set; } - public DateTime Created { get; set; } - public DateTime LastActivity { get; set; } + public DateTime Created { get; private set; } + public DateTime LastActivity { get; private set; } /// - /// Initializes a new instance of the class from a API JSON representation.*/ + /// Initializes a local instance of the class. + /// + /// The title of the room + /// The ID of the team owning the room + public Room(string title, string teamId) { + Title = title; + TeamId = teamId; + } + + /// + /// Initializes a new instance of the class. /// - /// The API returned JSON string - public Room(string json) { - var data = Json.Deserialize (json) as Dictionary; + /// Data. + Room(Dictionary data) { Id = (string)data ["id"]; Title = (string)data ["title"]; RoomType = (string)data ["type"]; IsLocked = (bool)data ["isLocked"]; + Created = DateTime.Parse ((string) data ["created"]); + LastActivity = DateTime.Parse ((string) data ["lastActivity"]); + object teamid; if (data.TryGetValue ("teamId", out teamid)) { TeamId = (string)teamid; @@ -33,21 +45,11 @@ public Room(string json) { } /// - /// Initializes a local instance of the class. - /// - /// The title of the room - /// The ID of the team owning the room - public Room(string title, string teamId) { - Title = title; - TeamId = teamId; - } - - /// - /// Commits the current state of the local Room object to Spark. - /// This will create a new room if it doesn't exist. + /// Commit the Room to the Spark Service /// - /// The created/updated Room from Spark - public IEnumerator Commit(Action callback) { + /// Error from Spark. + /// The resultant Spark Room. + public IEnumerator Commit(Action error, Action result) { // Setup request from current state of Room object var manager = GameObject.FindObjectOfType (); @@ -83,36 +85,56 @@ public IEnumerator Commit(Action callback) { if (www.isError) { Debug.LogError("Failed to Create Room: " + www.error); } else { - Room room = new Room (www.downloadHandler.text); - callback (room); + var roomData = Json.Deserialize (www.downloadHandler.text) as Dictionary; + if (roomData.ContainsKey ("message")) { + error (new SparkMessage (roomData)); + result (null); + } else { + result(new Room (roomData)); + error (null); + } } } } /// - /// Delete this Room on Spark. + /// Delete the specified Room on Spark. /// - public IEnumerator Delete() { + /// Error. + /// Success/Fail. + public IEnumerator Delete(Action error, Action result) { if (Id != null) { var manager = GameObject.FindObjectOfType (); using (UnityWebRequest www = manager.Generate ("rooms/" + Id, UnityWebRequest.kHttpVerbDELETE)) { yield return www.Send (); if (www.isError) { Debug.LogError ("Failed to Delete Room: " + www.error); + } else { + // Delete returns 204 on success + if (www.responseCode == 204) { + error (null); + result (true); + } else { + // Delete Failed + var json = Json.Deserialize (www.downloadHandler.text) as Dictionary; + error (new SparkMessage (json)); + result (false); + } } } } } /// - /// Lists the rooms matching the given filters you are a member of + /// Lists the rooms matching the given filters that you are a member of. /// - /// List of rooms - /// Show rooms belonging to a specific team - /// Maximum number of rooms to return - /// Type of room to search for - /// List of rooms - public static IEnumerator ListRooms(Action> callback, string teamId = null, int max = 0, string type = null) { + /// List of rooms. + /// Show rooms belonging to a specific team. + /// Maximum number of rooms to return. + /// Type of room to search for. + /// Any error from Spark. + /// List of rooms. + public static IEnumerator ListRooms(Action error, Action> result, string teamId = null, int max = 0, string type = null) { // Build Request var manager = GameObject.FindObjectOfType (); var data = new Dictionary (); @@ -122,10 +144,10 @@ public static IEnumerator ListRooms(Action> callback, string teamId = data ["teamId"] = teamId; } if (max != 0) { - data ["max"] = max.ToString (); + data ["max"] = max.ToString (); } if (type != null) { - data ["type"] = type; + data ["type"] = type; } // Make Request @@ -136,15 +158,20 @@ public static IEnumerator ListRooms(Action> callback, string teamId = Debug.LogError ("Failed to List Rooms: " + www.error); } else { // Convert to Room objects - var rooms = new List (); + var json = Json.Deserialize (www.downloadHandler.text) as Dictionary; - var items = json ["items"] as List; - foreach (Dictionary room_json in items) { - // TODO: Do I need to reconvert this? - string reJsoned = Json.Serialize (room_json); - rooms.Add(new Room (reJsoned)); + if (json.ContainsKey ("message")) { + error (new SparkMessage(json)); + result (null); + } else { + var rooms = new List (); + var items = json ["items"] as List; + foreach (Dictionary room_json in items) { + rooms.Add(new Room (room_json)); + } + result (rooms); + error (null); } - callback (rooms); } } } @@ -154,15 +181,23 @@ public static IEnumerator ListRooms(Action> callback, string teamId = /// /// The Room object /// Room identifier - /// Callback. - public static IEnumerator GetRoomDetails(string roomId, Action callback) { + /// Error from Spark, if any. + /// The returned Room from Spark. + public static IEnumerator GetRoomDetails(string roomId, Action error, Action result) { var manager = GameObject.FindObjectOfType (); using (UnityWebRequest www = manager.Generate ("rooms/" + roomId, UnityWebRequest.kHttpVerbGET)) { yield return www.Send (); if (www.isError) { Debug.LogError ("Failed to Retrieve Room: " + www.error); } else { - callback(new Room(www.downloadHandler.text)); + var roomDetails = Json.Deserialize (www.downloadHandler.text) as Dictionary; + if (roomDetails.ContainsKey ("message")) { + error (new SparkMessage (roomDetails)); + result (null); + } else { + result (new Room (roomDetails)); + error (null); + } } } } diff --git a/Spark SDK/Assets/Cisco/Spark SDK/Room.cs.meta b/SparkUnity/Assets/Cisco/Spark/Room.cs.meta similarity index 100% rename from Spark SDK/Assets/Cisco/Spark SDK/Room.cs.meta rename to SparkUnity/Assets/Cisco/Spark/Room.cs.meta diff --git a/SparkUnity/Assets/Cisco/Spark/SparkError.cs b/SparkUnity/Assets/Cisco/Spark/SparkError.cs new file mode 100644 index 0000000..9475e42 --- /dev/null +++ b/SparkUnity/Assets/Cisco/Spark/SparkError.cs @@ -0,0 +1,13 @@ +using UnityEngine; +using System.Collections.Generic; + +namespace Cisco.Spark { + public class SparkError { + + public string Description; + + public SparkError(Dictionary data) { + Description = data ["description"] as string; + } + } +} \ No newline at end of file diff --git a/Spark SDK/Assets/Spark.cs.meta b/SparkUnity/Assets/Cisco/Spark/SparkError.cs.meta similarity index 76% rename from Spark SDK/Assets/Spark.cs.meta rename to SparkUnity/Assets/Cisco/Spark/SparkError.cs.meta index 1f8451b..760bf06 100644 --- a/Spark SDK/Assets/Spark.cs.meta +++ b/SparkUnity/Assets/Cisco/Spark/SparkError.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: 91420adaf54bc4f318c1dd287b54fc80 -timeCreated: 1469454755 +guid: 3a242de3ddfcd46ac80727297b882fab +timeCreated: 1474973558 licenseType: Free MonoImporter: serializedVersion: 2 diff --git a/Spark SDK/Assets/Cisco/Spark SDK/SparkFile.cs b/SparkUnity/Assets/Cisco/Spark/SparkFile.cs similarity index 80% rename from Spark SDK/Assets/Cisco/Spark SDK/SparkFile.cs rename to SparkUnity/Assets/Cisco/Spark/SparkFile.cs index b6354f1..91fb093 100644 --- a/Spark SDK/Assets/Cisco/Spark SDK/SparkFile.cs +++ b/SparkUnity/Assets/Cisco/Spark/SparkFile.cs @@ -3,7 +3,6 @@ using System; using System.Collections; using System.Collections.Generic; -using MiniJSON; namespace Cisco.Spark { public class SparkFile { @@ -12,7 +11,7 @@ public class SparkFile { public byte[] Data { get; set;} // Return helper - public Type returnType; + public Type ReturnType; /// /// Initializes a new instance from a given Spark File ID. @@ -22,8 +21,9 @@ public SparkFile(string id) { Id = id; } - public SparkFile(byte[] data) { - + public SparkFile(string filename, byte[] data) { + Filename = filename; + Data = data; } /// @@ -32,31 +32,31 @@ public SparkFile(byte[] data) { /// File data as byte array /// Stops intelligent conversion of file array (e.g png -> Texture) and forces returning of a raw byte array. public IEnumerator Download(Action callback, bool forceBytes = false) { - Request manager = GameObject.FindObjectOfType (); + var manager = GameObject.FindObjectOfType (); using (UnityWebRequest www = manager.Generate ("contents/" + Id, UnityWebRequest.kHttpVerbGET)) { yield return www.Send (); if (www.isError) { Debug.LogError ("Failed to Download File: " + www.error); } else { - Dictionary headers = www.GetResponseHeaders (); + var headers = www.GetResponseHeaders (); Data = www.downloadHandler.data; Filename = headers ["Content-Disposition"].Split ('"')[1]; string fileType = Filename.Split ('.')[1]; if (fileType.Equals ("png") || fileType.Equals ("jpg")) { // Downloaded File is a supported image - Texture2D texture = new Texture2D (2,2); + var texture = new Texture2D (2,2); texture.LoadImage (Data); - returnType = typeof(UnityEngine.Texture2D); + ReturnType = typeof(UnityEngine.Texture2D); callback (texture); } else if (forceBytes) { // Asked for bytes - returnType = typeof(System.Byte); + ReturnType = typeof(Byte); callback (Data); } else { // TODO: Support more file types // No supported file type, returning bytes - returnType = typeof(System.Byte); + ReturnType = typeof(Byte); callback (Data); } } @@ -64,13 +64,13 @@ public IEnumerator Download(Action callback, bool forceBytes = false) { } public IEnumerator GetHeaders(Action> callback) { - Request manager = GameObject.FindObjectOfType (); + var manager = GameObject.FindObjectOfType (); using (UnityWebRequest www = manager.Generate ("contents/" + Id, UnityWebRequest.kHttpVerbHEAD)) { yield return www.Send (); if (www.isError) { Debug.LogError ("Failed to Download File Headers: " + www.error); } else { - Dictionary headers = www.GetResponseHeaders (); + var headers = www.GetResponseHeaders (); Filename = headers ["Content-Disposition"].Split ('"')[1]; callback (headers); } diff --git a/Spark SDK/Assets/Cisco/Spark SDK/SparkFile.cs.meta b/SparkUnity/Assets/Cisco/Spark/SparkFile.cs.meta similarity index 100% rename from Spark SDK/Assets/Cisco/Spark SDK/SparkFile.cs.meta rename to SparkUnity/Assets/Cisco/Spark/SparkFile.cs.meta diff --git a/SparkUnity/Assets/Cisco/Spark/SparkMessage.cs b/SparkUnity/Assets/Cisco/Spark/SparkMessage.cs new file mode 100644 index 0000000..4caccf7 --- /dev/null +++ b/SparkUnity/Assets/Cisco/Spark/SparkMessage.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; + +namespace Cisco.Spark { + public class SparkMessage { + public string Message; + public IEnumerable Errors; + public string TrackingId; + + public SparkMessage (Dictionary data) { + Message = (string) data ["message"]; + Errors = new List (); + + object errors; + if (data.TryGetValue ("errors", out errors)) { + var listOfErrors = errors as List; + var errorList = new List (); + foreach (var error in listOfErrors) { + errorList.Add (new SparkError(error as Dictionary)); + } + Errors = errorList; + } + TrackingId = (string) data ["trackingId"]; + } + } +} \ No newline at end of file diff --git a/SparkUnity/Assets/Cisco/Spark/SparkMessage.cs.meta b/SparkUnity/Assets/Cisco/Spark/SparkMessage.cs.meta new file mode 100644 index 0000000..6cbe26f --- /dev/null +++ b/SparkUnity/Assets/Cisco/Spark/SparkMessage.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 344003f9d6d534e46b75420e5f42bf98 +timeCreated: 1474972509 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Spark SDK/Assets/Cisco/Spark SDK/Team.cs b/SparkUnity/Assets/Cisco/Spark/Team.cs similarity index 50% rename from Spark SDK/Assets/Cisco/Spark SDK/Team.cs rename to SparkUnity/Assets/Cisco/Spark/Team.cs index bbdb6e8..6a17225 100644 --- a/Spark SDK/Assets/Cisco/Spark SDK/Team.cs +++ b/SparkUnity/Assets/Cisco/Spark/Team.cs @@ -9,12 +9,7 @@ namespace Cisco.Spark { public class Team { public string Id { get; private set;} public string Name { get; set;} - public string Created { get; private set;} - - /// - /// Internal use only. - /// - protected Team() { } + public DateTime Created { get; private set;} /// /// Initialize a new instance of the class locally. @@ -28,22 +23,20 @@ public Team(string name) { /// Initialize a new instance of the class from /// Spark (Not a constructor as usual due to parameter conflicts). /// - /// Json. - protected static Team FromSpark(string json) { - var team = new Team (); - var details = Json.Deserialize (json) as Dictionary; - team.Id = (string) details ["id"]; - team.Name = (string) details ["name"]; - team.Created = (string) details ["created"]; - return team; + /// Details from Spark. + Team(Dictionary details) { + Id = (string) details ["id"]; + Name = (string) details ["name"]; + Created = DateTime.Parse ((string) details ["created"]); } /// /// Commit the current state of the local object to Spark. /// This will create a new if it doesn't exist. /// - /// The created/updated from Spark - public IEnumerator Commit(Action callback) { + /// The error as from Spark + /// The created/updated from Spark + public IEnumerator Commit(Action error, Action result) { // Setup request from current state of Team object var manager = GameObject.FindObjectOfType (); @@ -72,22 +65,45 @@ public IEnumerator Commit(Action callback) { if (www.isError) { Debug.LogError("Failed to Create Team: " + www.error); } else { - var team = Team.FromSpark (www.downloadHandler.text); - callback (team); + // Parse Response + var teamData = Json.Deserialize (www.downloadHandler.text) as Dictionary; + if (teamData.ContainsKey ("message")) { + // Spark Error + error (new SparkMessage (teamData)); + result (null); + } else { + // Create local Team object + error(null); + result(new Team (teamData)); + } } } } /// - /// Delete this Team on the Spark service. + /// Delete the Team on Spark. /// - public IEnumerator Delete() { + /// Error. + /// Result. + public IEnumerator Delete(Action error, Action result) { if (Id != null) { var manager = GameObject.FindObjectOfType (); using (UnityWebRequest www = manager.Generate ("teams/" + Id, UnityWebRequest.kHttpVerbDELETE)) { yield return www.Send (); if (www.isError) { + // Network Error Debug.LogError ("Failed to Delete Team: " + www.error); + } else { + // Delete returns 204 on success + if (www.responseCode == 204) { + error (null); + result (true); + } else { + // Delete Failed + var json = Json.Deserialize (www.downloadHandler.text) as Dictionary; + error (new SparkMessage (json)); + result (false); + } } } } @@ -97,9 +113,10 @@ public IEnumerator Delete() { /// List the Teams the logged-in user is a member of. /// /// The Teams. - /// Callback. + /// Error from Spark (callback). + /// List of teams (callback). /// Max number of Teams to return. - public static IEnumerator ListTeams(Action> callback, int max = 0) { + public static IEnumerator ListTeams(Action error, Action> result, int max = 0) { var manager = GameObject.FindObjectOfType (); // Optional Parameters @@ -114,38 +131,59 @@ public static IEnumerator ListTeams(Action> callback, int max = 0) { // Make Request using (UnityWebRequest www = manager.Generate ("teams?" + queryString, UnityWebRequest.kHttpVerbGET)) { yield return www.Send (); + if (www.isError) { - Debug.LogError ("Failed to List Teams"); + // Network error + Debug.LogError("Failed to List Teams: " + www.error); } else { - // Convert to Team objects - var teams = new List (); + // Request succeeded, parse response var json = Json.Deserialize (www.downloadHandler.text) as Dictionary; - var items = json ["items"] as List; - foreach (var teamJson in items) { - string reJsoned = Json.Serialize (teamJson); - teams.Add(Team.FromSpark (reJsoned)); + + // Check for Spark side errors + if (json.ContainsKey ("message")) { + error (new SparkMessage (json)); + result (null); + } else { + // Convert to Team objects + var teams = new List (); + var items = json ["items"] as List; + foreach (var team in items) { + teams.Add (new Team (team as Dictionary)); + } + result (teams); + error (null); } - callback (teams); } } } - + /// - /// Retrieve a Team from Spark. + /// Gets the membership details. /// - /// The Team + /// The membership details. + /// Error. + /// Result. /// Team identifier. - /// Callback. - static public IEnumerator GetTeamDetails(string teamId, Action callback) { - var manager = GameObject.FindObjectOfType (); - using (UnityWebRequest www = manager.Generate("teams/" + teamId, UnityWebRequest.kHttpVerbGET)) { + public static IEnumerator GetTeamDetails(string teamId, Action error, Action result) { + Request manager = GameObject.FindObjectOfType (); + using (UnityWebRequest www = manager.Generate ("teams/" + teamId, UnityWebRequest.kHttpVerbGET)) { yield return www.Send (); + if (www.isError) { - Debug.LogError ("Failed to Get Team Details: " + www.error); - callback (null); + // Network error + Debug.LogError (www.error); } else { - var team = Team.FromSpark (www.downloadHandler.text); - callback (team); + // Parse Response + var teamData = Json.Deserialize (www.downloadHandler.text) as Dictionary; + if (teamData.ContainsKey ("team")) { + // Error Callback + error (new SparkMessage (teamData)); + result(null); + } else { + // Result callback + error (null); + result(new Team (teamData)); + } } } } diff --git a/Spark SDK/Assets/Cisco/Spark SDK/Team.cs.meta b/SparkUnity/Assets/Cisco/Spark/Team.cs.meta similarity index 100% rename from Spark SDK/Assets/Cisco/Spark SDK/Team.cs.meta rename to SparkUnity/Assets/Cisco/Spark/Team.cs.meta diff --git a/SparkUnity/Assets/Cisco/Spark/TeamMembership.cs b/SparkUnity/Assets/Cisco/Spark/TeamMembership.cs new file mode 100644 index 0000000..fec7bce --- /dev/null +++ b/SparkUnity/Assets/Cisco/Spark/TeamMembership.cs @@ -0,0 +1,232 @@ +using UnityEngine; +using UnityEngine.Networking; +using System; +using System.Collections; +using System.Collections.Generic; +using MiniJSON; + +namespace Cisco.Spark { + public class TeamMembership { + public string Id { get; private set; } + public string TeamId { get; set; } + public string PersonId { get; set; } + public string PersonEmail { get; set; } + public string PersonDisplayName { get; set; } + public bool IsModerator { get; set; } + public DateTime Created { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public TeamMembership() { } + + /// + /// Initializes a new instance of the class. + /// + /// Team identifier. + /// Person identifier. + /// Person email. + /// If set to true is moderator. + public TeamMembership(string teamId, string personId=null, string personEmail=null, bool isModerator=false) { + // Argument checking + if (personId == null && personEmail == null) { + throw new ArgumentNullException ("personId","One of PersonId and PersonEmail must be given"); + } + TeamId = teamId; + PersonId = personId; + PersonEmail = personEmail; + IsModerator = isModerator; + } + + /// + /// Initializes a new instance of from Spark. + /// + /// Team Membership data. + TeamMembership(Dictionary teamMembershipData) { + try { + Id = teamMembershipData ["id"] as string; + TeamId = teamMembershipData ["teamId"] as string; + PersonId = teamMembershipData ["personId"] as string; + PersonEmail = teamMembershipData ["personEmail"] as string; + PersonDisplayName = teamMembershipData ["personDisplayName"] as string; + IsModerator = (bool) teamMembershipData ["isModerator"]; + Created = DateTime.Parse ((string) teamMembershipData ["created"]); + } catch (KeyNotFoundException) { + Debug.Log ("Couldn't parse Team Membership"); + } + } + + /// + /// Commit the specified error and result. + /// + /// Error. + /// Result. + public IEnumerator Commit(Action error, Action result) { + var manager = GameObject.FindObjectOfType (); + + // Membership Data + var data = new Dictionary (); + + // Create or Update? + string resource; + string httpVerb; + if (Id == null) { + // Creating a new Membership + data ["teamId"] = TeamId; + data ["personId"] = PersonId; + data ["personEmail"] = PersonEmail; + data ["isModerator"] = IsModerator.ToString (); + resource = "team/memberships"; + httpVerb = UnityWebRequest.kHttpVerbPOST; + } else { + // Updating an existing Membership + // Only changing is currently supported. + data ["isModerator"] = IsModerator.ToString (); + resource = "team/memberships/" + Id; + httpVerb = UnityWebRequest.kHttpVerbPUT; + } + + // Make request + using (UnityWebRequest www = manager.Generate(resource, httpVerb)) { + byte[] raw_data = System.Text.Encoding.UTF8.GetBytes (Json.Serialize (data)); + www.uploadHandler = new UploadHandlerRaw (raw_data); + yield return www.Send (); + if (www.isError) { + Debug.LogError("Failed to Create Team Membership: " + www.error); + } else { + // Parse Response + var teamMembershipData = Json.Deserialize (www.downloadHandler.text) as Dictionary; + if (teamMembershipData.ContainsKey ("message")) { + // Spark Error + error (new SparkMessage (teamMembershipData)); + result (null); + } else { + // Create local TeamMembership object + error(null); + result(new TeamMembership (teamMembershipData)); + } + } + } + } + + /// + /// Delete the specified error and result. + /// + /// Error. + /// Result. + public IEnumerator Delete(Action error, Action result) { + if (Id != null) { + var manager = GameObject.FindObjectOfType (); + using (UnityWebRequest www = manager.Generate ("team/memberships/" + Id, UnityWebRequest.kHttpVerbDELETE)) { + yield return www.Send (); + if (www.isError) { + // Network Error + Debug.LogError ("Failed to Delete Team Membership: " + www.error); + } else { + // Delete returns 204 on success + if (www.responseCode == 204) { + error (null); + result (true); + } else { + // Delete Failed + var json = Json.Deserialize (www.downloadHandler.text) as Dictionary; + error (new SparkMessage (json)); + result (false); + } + } + } + } + } + + /// + /// Lists the memberships. + /// + /// The memberships. + /// Error. + /// Result. + /// Team identifier. + /// Person identifier. + /// Person email. + /// Max. + public static IEnumerator ListTeamMemberships(Action error, Action> result, string teamId = null, string personId = null, string personEmail = null, int max = 0) { + var manager = GameObject.FindObjectOfType (); + + // Optional Parameters + var data = new Dictionary (); + if (teamId != null) { + data ["teamId"] = teamId; + } + if (personId != null) { + data ["personId"] = personId; + } + if (personEmail != null) { + data ["personEmail"] = personEmail; + } + if (max != 0) { + data ["max"] = max.ToString (); + } + + // Optional Parameters to URL query + string queryString = System.Text.Encoding.UTF8.GetString (UnityWebRequest.SerializeSimpleForm (data)); + + // Make Request + using (UnityWebRequest www = manager.Generate ("team/memberships?" + queryString, UnityWebRequest.kHttpVerbGET)) { + yield return www.Send (); + + if (www.isError) { + // Network error + Debug.LogError("Failed to List Team Memberships: " + www.error); + } else { + // Request succeeded, parse response + var json = Json.Deserialize (www.downloadHandler.text) as Dictionary; + + // Check for Spark side errors + if (json.ContainsKey ("message")) { + error (new SparkMessage (json)); + result (null); + } else { + // Convert to TeamMembership objects + var teamMemberships = new List (); + var items = json ["items"] as List; + foreach (var teamMembership in items) { + teamMemberships.Add (new TeamMembership (teamMembership as Dictionary)); + } + result (teamMemberships); + error (null); + } + } + } + } + + /// + /// Gets the membership details. + /// + /// The membership details. + /// Error. + /// Result. + /// Membership identifier. + public static IEnumerator GetTeamMembershipDetails(string teamMembershipId, Action error, Action result) { + Request manager = GameObject.FindObjectOfType (); + using (UnityWebRequest www = manager.Generate ("team/memberships/" + teamMembershipId, UnityWebRequest.kHttpVerbGET)) { + yield return www.Send (); + + if (www.isError) { + // Network error + Debug.LogError (www.error); + } else { + // Parse Response + var teamMembershipData = Json.Deserialize (www.downloadHandler.text) as Dictionary; + if (teamMembershipData.ContainsKey ("message")) { + // Error Callback + error (new SparkMessage (teamMembershipData)); + result(null); + } else { + // Result callback + error (null); + result(new TeamMembership (teamMembershipData)); + } + } + } + } + } +} diff --git a/Spark SDK/Assets/Cisco/Spark SDK/TeamMembership.cs.meta b/SparkUnity/Assets/Cisco/Spark/TeamMembership.cs.meta similarity index 100% rename from Spark SDK/Assets/Cisco/Spark SDK/TeamMembership.cs.meta rename to SparkUnity/Assets/Cisco/Spark/TeamMembership.cs.meta diff --git a/Spark SDK/Assets/Tests.meta b/SparkUnity/Assets/Cisco/Spark/Tests.meta similarity index 100% rename from Spark SDK/Assets/Tests.meta rename to SparkUnity/Assets/Cisco/Spark/Tests.meta diff --git a/SparkUnity/Assets/Cisco/Spark/Tests/TestMembership.cs b/SparkUnity/Assets/Cisco/Spark/Tests/TestMembership.cs new file mode 100644 index 0000000..70b593d --- /dev/null +++ b/SparkUnity/Assets/Cisco/Spark/Tests/TestMembership.cs @@ -0,0 +1,121 @@ +using UnityEngine; +using Cisco.Spark; + +public class TestMembership : MonoBehaviour { + // Use this for initialization + void Start () + { + // Error Tracking + var errorCount = 0; + + // Create a new room for testing + var testRoom = new Room ("Test Room (CiscoSpark-UnitySDK", null); + StartCoroutine (testRoom.Commit (error => { + if (error != null) { + Debug.LogError("Could not create target Room: " + error.Message); + errorCount++; + } + }, room => { + Debug.Log("Created target Room!"); + testRoom = room; + if (testRoom.Id == null) { + Debug.LogError ("Could not create target Room"); + errorCount++; + } + + // Create the membership + var membership = new Membership (); + membership.RoomId = testRoom.Id; + membership.PersonId = "Y2lzY29zcGFyazovL3VzL1BFT1BMRS9iYzJkMjY2YS1jYjI4LTRkZDItOTBkOC1kMzllYjc0OWZlYTU"; + + // Commit the membership to Spark + StartCoroutine (membership.Commit (commitError => { + if (commitError != null) { + Debug.LogError ("Couldn't commit membership: " + commitError.Message); + errorCount++; + } + }, commitResponse => { + Debug.Log("Created membership on Spark!"); + membership = commitResponse; + if (membership.Id == null) { + Debug.LogError ("Couldn't commit membership"); + errorCount++; + } + + // Try and commit empty membership + StartCoroutine (new Membership ().Commit (emptyCommitError => { + // This should error + if (emptyCommitError != null) { + if (!emptyCommitError.Message.Equals ("roomId cannot be null")) { + Debug.LogError ("Empty RoomId test failed"); + } + } else { + Debug.Log("Empty RoomId test passed!"); + } + + // Get Membership Details + StartCoroutine (Membership.GetMembershipDetails (membership.Id, membershipDetailsError => { + if (membershipDetailsError != null) { + Debug.LogError ("GetMembership Details failed: " + membershipDetailsError.Message); + errorCount++; + } + }, membershipDetails => { + membership = membershipDetails; + if (membershipDetails.Id != membership.Id) { + Debug.LogError ("Couldn't retrieve membership details"); + errorCount++; + } else { + Debug.Log ("Get Membership Details passed!"); + } + + // List Memberships + StartCoroutine (Membership.ListMemberships (listMembershipsError => { + if (listMembershipsError != null) { + Debug.LogError("Couldn't list memberships: " + listMembershipsError.Message); + errorCount++; + } + }, memberships => { + Debug.Log("List Memberships passed!"); + + // Convert to moderator + membership.IsModerator = true; + StartCoroutine (membership.Commit(moderatedError => { + if (moderatedError != null) { + Debug.LogError("Couldn't set moderator flag: " + moderatedError.Message); + errorCount++; + } + }, moderatedMembership => { + membership = moderatedMembership; + if (!membership.IsModerator) { + Debug.LogError("Couldn't set moderator flag"); + errorCount++; + } else { + Debug.Log("Edit Membership passed!"); + } + + // Clean up membership + StartCoroutine (membership.Delete (deleteError => { + if (deleteError != null) { + Debug.Log("Couldn't delete membership: " + deleteError.Message); + errorCount++; + } + }, deleted => StartCoroutine (testRoom.Delete (deleteRoomError => { + if (deleteRoomError != null) { + Debug.LogError ("Couldn't delete target Room: " + deleteRoomError.Message); + errorCount++; + } + }, deleteRoom => { + if (errorCount > 0) { + Debug.LogError (errorCount + " tests failed"); + } else { + Debug.Log ("All Membership tests passed"); + } + })))); + })); + }, testRoom.Id)); + })); + }, res => {})); + })); + })); + } +} \ No newline at end of file diff --git a/SparkUnity/Assets/Cisco/Spark/Tests/TestMembership.cs.meta b/SparkUnity/Assets/Cisco/Spark/Tests/TestMembership.cs.meta new file mode 100644 index 0000000..92c76cf --- /dev/null +++ b/SparkUnity/Assets/Cisco/Spark/Tests/TestMembership.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 12a5abf165e3e4f3e9d11f9819adc900 +timeCreated: 1471443382 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/SparkUnity/Assets/Cisco/Spark/Tests/TestMessage.cs b/SparkUnity/Assets/Cisco/Spark/Tests/TestMessage.cs new file mode 100644 index 0000000..d6006ea --- /dev/null +++ b/SparkUnity/Assets/Cisco/Spark/Tests/TestMessage.cs @@ -0,0 +1,93 @@ +using UnityEngine; +using Cisco.Spark; + +/// +/// Class to test the functionality. +/// +public class TestMessage : MonoBehaviour { + + // Run all tests + void Start () { + int errorCount = 0; + Debug.Log ("Running Message tests"); + + // Create a room for tests + var testRoom = new Room ("Test Room (CiscoSpark-UnitySDK", null); + StartCoroutine (testRoom.Commit (error => { + // Failed to create test room + if (error != null) { + errorCount++; + Debug.LogError("Couldn't create target Room"); + } + }, room => { + // Test Room successful + testRoom = room; + if (testRoom.Id == null) { + errorCount++; + Debug.LogError ("Could not create target Room"); + } + + // Create a new Message + var newMessage = new Message (); + newMessage.RoomId = testRoom.Id; + newMessage.Text = "Test message"; + + // Commit Message + StartCoroutine (newMessage.Commit (error => { + if (error != null) { + errorCount++; + Debug.LogError("Couldn't commit Message: " + error.Message); + } + }, message => { + newMessage = message; + + // List Messages From Room + StartCoroutine (Message.ListMessages (testRoom.Id, error => { + if (error != null) { + errorCount++; + Debug.LogError("Couldn't list Messages: " + error.Message); + } + }, messages => { + // Check Message was successful + if (messages [messages.Count - 1].Text != "Test message") { + Debug.LogError ("Create Message Failed!"); + errorCount++; + } else { + Debug.Log ("Create Message Passed!"); + } + + // Delete the message + StartCoroutine (newMessage.Delete (error => { + if (error != null) { + errorCount++; + Debug.LogError("Couldn't delete message: " + error.Message); + } + }, result => StartCoroutine (Message.GetMessageDetails (newMessage.Id, error => { + // This should error as the message has been deleted + Debug.Log ("Successfully failed to get deleted message details"); + // Clean up test Room + StartCoroutine (testRoom.Delete (deleteError => { + if (deleteError != null) { + errorCount++; + Debug.LogError ("Couldn't delete Test Room: " + deleteError.Message); + } + }, delete => { + // Finish and Report + Debug.Log ("Finished Running Message Tests"); + if (errorCount == 0) { + Debug.Log ("All tests passed!"); + } else { + Debug.LogError (errorCount + " tests failed!"); + } + })); + }, success => { + if (success != null) { + errorCount++; + Debug.Log ("Shouldn't be able to get details for deleted message"); + } + })))); + })); + })); + })); + } +} diff --git a/SparkUnity/Assets/Cisco/Spark/Tests/TestMessage.cs.meta b/SparkUnity/Assets/Cisco/Spark/Tests/TestMessage.cs.meta new file mode 100644 index 0000000..574adfd --- /dev/null +++ b/SparkUnity/Assets/Cisco/Spark/Tests/TestMessage.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 496ddf05a7370416591de44cd97004fd +timeCreated: 1470157661 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/SparkUnity/Assets/Cisco/Spark/Tests/TestPerson.cs b/SparkUnity/Assets/Cisco/Spark/Tests/TestPerson.cs new file mode 100644 index 0000000..14bc2ff --- /dev/null +++ b/SparkUnity/Assets/Cisco/Spark/Tests/TestPerson.cs @@ -0,0 +1,66 @@ +using UnityEngine; +using Cisco.Spark; + +public class TestPerson : MonoBehaviour { + + void Start() { + // Error Count + var errorCount = 0; + StartCoroutine (Person.ListPeople (listPeopleError => { + if (listPeopleError != null) { + errorCount++; + Debug.LogError("List people failed: " + listPeopleError.Message); + } + }, people => { + if (people.Count != 1) { + errorCount++; + Debug.LogError("rilogan@cisco.com should return 1 record"); + } + + if (people[0].DisplayName != "Rich Logan") { + errorCount++; + Debug.Log("List People not returning details correctly"); + } + + StartCoroutine (Person.GetPersonDetails (people[0].Id, getPersonError => { + if (getPersonError != null) { + errorCount++; + Debug.LogError("Get Person Details failed: " + getPersonError.Message); + } + },person => { + if (person.DisplayName != "Rich Logan") { + errorCount++; + Debug.Log("Display name not retrieved correctly"); + } + + StartCoroutine (Person.GetPersonDetails (getPersonAgain => { + if (getPersonAgain != null) { + errorCount++; + Debug.LogError("Get Person Details failed: " + getPersonAgain.Message); + } + }, callback => { + Debug.Log("Authenticated user is: " + callback.DisplayName); + + StartCoroutine (person.DownloadAvatar (avatarError => { + if (avatarError != null) { + errorCount++; + Debug.LogError("List people failed: " + avatarError.Message); + } + }, texture => { + if (texture.width <= 0) { + Debug.Log("Couldn't download user's avatar"); + errorCount++; + } + + // Finish + if (errorCount > 0) { + Debug.LogError("Some tests failed"); + } else { + Debug.Log ("All tests passed"); + } + })); + })); + })); + }, "rilogan@cisco.com")); + } +} diff --git a/SparkUnity/Assets/Cisco/Spark/Tests/TestPerson.cs.meta b/SparkUnity/Assets/Cisco/Spark/Tests/TestPerson.cs.meta new file mode 100644 index 0000000..07714b6 --- /dev/null +++ b/SparkUnity/Assets/Cisco/Spark/Tests/TestPerson.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 893d2ca12b2bb489cbad15067612687c +timeCreated: 1474449180 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/SparkUnity/Assets/Cisco/Spark/Tests/TestRoom.cs b/SparkUnity/Assets/Cisco/Spark/Tests/TestRoom.cs new file mode 100644 index 0000000..c1a3e36 --- /dev/null +++ b/SparkUnity/Assets/Cisco/Spark/Tests/TestRoom.cs @@ -0,0 +1,100 @@ +using UnityEngine; +using Cisco.Spark; + +/// +/// Class to test the functionality. +/// +public class TestRoom : MonoBehaviour { + + void Start () { + int errorCount = 0; + Debug.Log ("Running Room tests"); + + // List Rooms + StartCoroutine (Room.ListRooms (listRoomsError => { + if (listRoomsError != null) { + Debug.LogError("Couldn't list Rooms: " + listRoomsError.Message); + errorCount++; + } + }, rooms => { + Debug.Log("List Rooms Passed!"); + int startRoomCount = rooms.Count; + + // Create New Room + var testRoom = new Room("Test Room (CiscoSpark-UnitySDK)", null); + StartCoroutine(testRoom.Commit (commitRoomError => { + if (commitRoomError != null) { + Debug.LogError("Couldn't commit Room: " + commitRoomError.Message); + errorCount++; + } + }, room => { + testRoom = room; + if (testRoom.Title != "Test Room (CiscoSpark-UnitySDK)") { + Debug.LogError("Create Room Failed!"); + errorCount++; + } else { + Debug.Log("Create Room Passed!"); + } + + // Edit Room with Updated Title + testRoom.Title = "Updated Test Room (CiscoSpark-UnitySDK)"; + StartCoroutine (testRoom.Commit(updateRoomError => { + if (updateRoomError != null) { + Debug.LogError("Couldn't update Room: " + updateRoomError.Message); + errorCount++; + } + }, updatedRoom => { + testRoom = updatedRoom; + if (testRoom.Title != "Updated Test Room (CiscoSpark-UnitySDK)") { + Debug.LogError ("Update Room Failed!"); + errorCount++; + } else { + Debug.Log("Update Room Passed!"); + } + + // Get Room Details + StartCoroutine (Room.GetRoomDetails (testRoom.Id, roomDetailsError => { + if (roomDetailsError != null) { + Debug.LogError("Couldn't get Room details: " + roomDetailsError.Message); + errorCount++; + } + }, retrivedRoom => { + testRoom = retrivedRoom; + if (testRoom.Title != "Updated Test Room (CiscoSpark-UnitySDK)") { + Debug.LogError ("GetRoomDetails Failed!"); + } else { + Debug.Log("GetRoomDetails Passed!"); + } + + // Delete Room + StartCoroutine (testRoom.Delete (deleteRoomError => { + if (deleteRoomError != null) { + Debug.LogError("Couldn't get Room details: " + deleteRoomError.Message); + errorCount++; + } + }, result => StartCoroutine (Room.ListRooms (listRoomsError => { + if (listRoomsError != null) { + Debug.LogError ("Couldn't list Rooms: " + listRoomsError.Message); + errorCount++; + } + }, postDeleteRooms => { + if (startRoomCount != postDeleteRooms.Count) { + Debug.LogError ("Delete Room Failed!"); + errorCount++; + } else { + Debug.Log ("Delete Room Passed!"); + } + // Finish and Report + Debug.Log ("Finished Running Room Tests"); + if (errorCount == 0) { + Debug.Log ("All tests passed!"); + } else { + Debug.LogError (errorCount + " tests failed!"); + } + })))); + })); + })); + })); + })); + } +} diff --git a/Spark SDK/Assets/Tests/TestRoom.cs.meta b/SparkUnity/Assets/Cisco/Spark/Tests/TestRoom.cs.meta similarity index 100% rename from Spark SDK/Assets/Tests/TestRoom.cs.meta rename to SparkUnity/Assets/Cisco/Spark/Tests/TestRoom.cs.meta diff --git a/SparkUnity/Assets/Cisco/Spark/Tests/TestTeam.cs b/SparkUnity/Assets/Cisco/Spark/Tests/TestTeam.cs new file mode 100644 index 0000000..72bf932 --- /dev/null +++ b/SparkUnity/Assets/Cisco/Spark/Tests/TestTeam.cs @@ -0,0 +1,88 @@ +using UnityEngine; +using Cisco.Spark; + +public class TestTeam : MonoBehaviour { + void Start () { + var errorCount = 0; + // List Teams + StartCoroutine (Team.ListTeams (listError => { + if (listError != null) { + errorCount++; + Debug.LogError ("List Teams failed: " + listError.Message); + } + }, teams => { + if (teams.Count > 0) { + Debug.Log ("ListTeams passed"); + } else { + errorCount++; + Debug.LogError ("ListTeams failed"); + } + + // Get Team Details + StartCoroutine (Team.GetTeamDetails (teams[0].Id, detailsError => { + if (detailsError != null) { + errorCount++; + Debug.LogError ("List Teams failed: " + detailsError.Message); + } + }, retrievedTeam => { + if (retrievedTeam.Name != teams[0].Name) { + errorCount++; + Debug.LogError("GetTeamDetails failed"); + } else { + Debug.Log("GetTeamDetails passed"); + } + + // Create Team + const string OriginalName = "Unity SDK Test Team"; + var t = new Team(OriginalName); + StartCoroutine (t.Commit (commitError => { + if (commitError != null) { + errorCount++; + Debug.LogError("GetTeamDetails failed: " + commitError.Message); + } + }, createdTeam => { + t = createdTeam; + if (t.Id == null) { + errorCount++; + Debug.LogError ("Create Team failed"); + } else { + Debug.Log("Create Team passed"); + } + + // Update Team + const string NewName = OriginalName + "RENAMED"; + t.Name = NewName; + StartCoroutine (t.Commit (updateError => { + if (updateError != null) { + errorCount++; + Debug.LogError("Update failed: " + updateError.Message); + } + }, updatedTeam => { + t = updatedTeam; + if (t.Name != NewName) { + errorCount++; + Debug.LogError ("Update Team failed"); + } else { + Debug.Log("Update Team passed"); + } + + // Delete Team + StartCoroutine (t.Delete (deleteError => { + if (deleteError != null) { + errorCount++; + Debug.LogError("Delete team failed: " + deleteError.Message); + } + }, deleteSuccess => { + // Error Report + if (errorCount > 0) { + Debug.LogError(errorCount + " Team tests failed"); + } else { + Debug.Log("All Team tests passed"); + } + })); + })); + })); + })); + })); + } +} diff --git a/SparkUnity/Assets/Cisco/Spark/Tests/TestTeam.cs.meta b/SparkUnity/Assets/Cisco/Spark/Tests/TestTeam.cs.meta new file mode 100644 index 0000000..d58aad5 --- /dev/null +++ b/SparkUnity/Assets/Cisco/Spark/Tests/TestTeam.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 86502575eb0d848fc9e81dc30c691bab +timeCreated: 1474459530 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/SparkUnity/Assets/Cisco/Spark/Tests/TestTeamMembership.cs b/SparkUnity/Assets/Cisco/Spark/Tests/TestTeamMembership.cs new file mode 100644 index 0000000..dd5c795 --- /dev/null +++ b/SparkUnity/Assets/Cisco/Spark/Tests/TestTeamMembership.cs @@ -0,0 +1,120 @@ +using UnityEngine; +using Cisco.Spark; + +public class TestTeamMembership : MonoBehaviour { + // Use this for initialization + void Start () + { + // Error Tracking + var errorCount = 0; + + var testTeam = new Team ("SparkUnityTestTeam"); + StartCoroutine (testTeam.Commit (error => { + if (error != null) { + Debug.LogError("Could not create target Team: " + error.Message); + errorCount++; + } + }, team => { + Debug.Log("Created target Team!"); + testTeam = team; + if (testTeam.Id == null) { + Debug.LogError ("Could not create target Team"); + errorCount++; + } + + // Create the membership + var teamMembership = new TeamMembership (); + teamMembership.TeamId = testTeam.Id; + teamMembership.PersonId = "Y2lzY29zcGFyazovL3VzL1BFT1BMRS9iYzJkMjY2YS1jYjI4LTRkZDItOTBkOC1kMzllYjc0OWZlYTU"; + + // Commit the membership to Spark + StartCoroutine (teamMembership.Commit (commitError => { + if (commitError != null) { + Debug.LogError ("Couldn't commit team membership: " + commitError.Message); + errorCount++; + } + }, commitResponse => { + Debug.Log("Created team membership on Spark!"); + teamMembership = commitResponse; + if (teamMembership.Id == null) { + Debug.LogError ("Couldn't commit membership"); + errorCount++; + } + + // Try and commit empty membership + StartCoroutine (new TeamMembership ().Commit (emptyCommitError => { + // This should error + if (emptyCommitError != null) { + if (!emptyCommitError.Message.Equals ("teamId cannot be null")) { + Debug.LogError ("Empty TeamId test failed"); + } + } else { + Debug.Log("Empty TeamId test passed!"); + } + + // Get Membership Details + StartCoroutine (TeamMembership.GetTeamMembershipDetails (teamMembership.Id, membershipDetailsError => { + if (membershipDetailsError != null) { + Debug.LogError ("Get Team Membership Details failed: " + membershipDetailsError.Message); + errorCount++; + } + }, membershipDetails => { + teamMembership = membershipDetails; + if (membershipDetails.Id != teamMembership.Id) { + Debug.LogError ("Couldn't retrieve membership details"); + errorCount++; + } else { + Debug.Log ("Get Team Membership Details passed!"); + } + + // List Memberships + StartCoroutine (TeamMembership.ListTeamMemberships (listMembershipsError => { + if (listMembershipsError != null) { + Debug.LogError("Couldn't list team memberships: " + listMembershipsError.Message); + errorCount++; + } + }, memberships => { + Debug.Log("List Team Memberships passed!"); + + // Convert to moderator + teamMembership.IsModerator = true; + StartCoroutine (teamMembership.Commit(moderatedError => { + if (moderatedError != null) { + Debug.LogError("Couldn't set moderator flag: " + moderatedError.Message); + errorCount++; + } + }, moderatedMembership => { + teamMembership = moderatedMembership; + if (!teamMembership.IsModerator) { + Debug.LogError("Couldn't set moderator flag"); + errorCount++; + } else { + Debug.Log("Edit Team Membership passed!"); + } + + // Clean up membership + StartCoroutine (teamMembership.Delete (deleteError => { + if (deleteError != null) { + Debug.Log("Couldn't delete membership: " + deleteError.Message); + errorCount++; + } + }, deleted => StartCoroutine (testTeam.Delete (deleteTeamError => { + if (deleteTeamError != null) { + Debug.LogError ("Couldn't delete target Team: " + deleteTeamError.Message); + errorCount++; + } + }, deleteTeam => { + if (errorCount > 0) { + Debug.LogError (errorCount + " tests failed"); + } else { + Debug.Log ("All Team Membership tests passed"); + } + })))); + })); + }, testTeam.Id)); + })); + }, res => {})); + })); + })); + } +} \ No newline at end of file diff --git a/SparkUnity/Assets/Cisco/Spark/Tests/TestTeamMembership.cs.meta b/SparkUnity/Assets/Cisco/Spark/Tests/TestTeamMembership.cs.meta new file mode 100644 index 0000000..6bde026 --- /dev/null +++ b/SparkUnity/Assets/Cisco/Spark/Tests/TestTeamMembership.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 2796799f2cde64d02b143a4b0abe2892 +timeCreated: 1477585053 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/SparkUnity/Assets/Cisco/Spark/Tests/TestWebhook.cs b/SparkUnity/Assets/Cisco/Spark/Tests/TestWebhook.cs new file mode 100644 index 0000000..7c52fb3 --- /dev/null +++ b/SparkUnity/Assets/Cisco/Spark/Tests/TestWebhook.cs @@ -0,0 +1,84 @@ +using UnityEngine; +using Cisco.Spark; + +public class TestWebhook : MonoBehaviour { + + // Use this for initialization + void Start () { + var errorCount = 0; + + // List Webhooks + StartCoroutine (Webhook.ListWebhooks (listWebhookError => { + if (listWebhookError != null) { + errorCount++; + Debug.LogError ("List Webhooks failed: " + listWebhookError.Message); + } + }, webhooks => { + // List Webhooks Passed + var newWebhook = new Webhook ( + "testingWebhook", + "http://example.org", + "messages", + "created", + "86dacc007724d8ea666f88fc77d918dad9537a15" + ); + StartCoroutine (newWebhook.Commit (commitError => { + if (commitError != null) { + errorCount++; + Debug.LogError("Create Webhook failed: " + commitError.Message); + } + }, commitedWebhook => { + newWebhook = commitedWebhook; + if (newWebhook.Id == null) { + Debug.LogError("ID wasn't set. This shouldn't happen."); + } else { + // Create Passed + Debug.Log("Create Webhook Passed"); + newWebhook.Name = "testingWebhookUpdated"; + StartCoroutine (newWebhook.Commit (updateError => { + if (updateError != null) { + errorCount++; + Debug.Log("Update Webhook failed: " + updateError.Message); + } + }, updatedWebhook => { + newWebhook = updatedWebhook; + if (newWebhook.Name != "testingWebhookUpdated") { + errorCount++; + Debug.LogError("Couldn't update Webhook Name"); + } else { + // Update Passed + Debug.Log("Update Webhook Passed"); + + StartCoroutine (Webhook.GetWebhookDetails (newWebhook.Id, detailsError => { + if (detailsError != null) { + errorCount++; + Debug.LogError("Couldn't get Webhook details: " + detailsError.Message); + } + }, webhook => { + if (newWebhook.Id != webhook.Id) { + errorCount++; + Debug.LogError ("Retrieve webhook ID doesn't match"); + } else { + // Details Passed + StartCoroutine (newWebhook.Delete (deleteError => { + if (deleteError != null) { + errorCount++; + Debug.LogError ("Delete Webhook failed: " + deleteError.Message); + } + }, delete => { + // Finish and Report + if (errorCount == 0) { + Debug.Log("All tests passed"); + } else { + Debug.LogError(errorCount + " tests failed"); + } + })); + } + })); + } + })); + } + })); + })); + } +} diff --git a/SparkUnity/Assets/Cisco/Spark/Tests/TestWebhook.cs.meta b/SparkUnity/Assets/Cisco/Spark/Tests/TestWebhook.cs.meta new file mode 100644 index 0000000..a791b59 --- /dev/null +++ b/SparkUnity/Assets/Cisco/Spark/Tests/TestWebhook.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 3bcd26b882ebf4bd9b3be74c75d2c75c +timeCreated: 1474971192 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/SparkUnity/Assets/Cisco/Spark/Webhook.cs b/SparkUnity/Assets/Cisco/Spark/Webhook.cs new file mode 100644 index 0000000..c17549d --- /dev/null +++ b/SparkUnity/Assets/Cisco/Spark/Webhook.cs @@ -0,0 +1,239 @@ +using UnityEngine; +using UnityEngine.Networking; +using System; +using System.Collections; +using System.Collections.Generic; +using MiniJSON; + +namespace Cisco.Spark { + public class Webhook { + public string Id { get; private set; } + public string Name { get; set; } + public string TargetUrl { get; set;} + public string Resource { get; set; } + public string Event { get; set; } + public string Filter { get; set; } + public string OrgId { get; set; } + public string CreatedBy { get; set; } + public string AppId { get; set; } + public string Secret { get; set; } + public DateTime Created { get; private set; } + + + /// + /// Initializes a new instance of the class from a API JSON representation.*/ + /// + /// The API returned Webhook data + public Webhook(Dictionary data) { + Id = (string)data ["id"]; + Name = (string)data ["name"]; + TargetUrl = (string)data ["targetUrl"]; + Resource = (string)data ["resource"]; + Event = (string)data ["event"]; + OrgId = (string)data ["orgId"]; + CreatedBy = (string)data ["createdBy"]; + AppId = (string)data ["appId"]; + Created = DateTime.Parse ((string)data ["created"]); + + // Filter may not always be retrieved + object filter; + if (data.TryGetValue ("filter", out filter)) { + Filter = (string)data ["filter"]; + } + + // Secret may not always be retrieved + object secret; + if (data.TryGetValue ("secret", out secret)) { + Secret = (string)data ["secret"]; + } + } + + /// + /// Initializes a new instance of the class locally. + /// + /// Name. + /// Target URL. + /// Resource. + /// Event. + /// Secret. + /// Filter. + public Webhook(string name, string targetUrl, string resource, string @event, string secret, string filter = null) { + Name = name; + TargetUrl = targetUrl; + Resource = resource; + Event = @event; + Secret = secret; + Filter = filter; + } + + /// + /// Commits the current state of the local Webhook object to Spark. + /// This will create a new webhook if it doesn't exist. + /// + /// The error or message from Spark (if any) + /// The created/updated Webhook from Spark (if any) + public IEnumerator Commit(Action error, Action result) { + // Setup request from current state of Webhook object + var manager = GameObject.FindObjectOfType (); + + // Webhook Data + var data = new Dictionary (); + data ["name"] = Name; + data ["targetUrl"] = TargetUrl; + data ["resource"] = Resource; + data ["event"] = Event; + data ["secret"] = Secret; + // Filter is optional + if (Filter != null) { + data ["filter"] = Filter; + } + + // Create or Update? + string resource; + string httpVerb; + if (Id == null) { + // Creating a new webhook + resource = "webhooks"; + httpVerb = UnityWebRequest.kHttpVerbPOST; + } else { + // Updating a previous webhook + // Only changing name and targetUrl is currently + // supported + data.Remove ("resource"); + data.Remove ("event"); + data.Remove ("secret"); + data.Remove ("filter"); + resource = "webhooks/" + Id; + httpVerb = UnityWebRequest.kHttpVerbPUT; + } + + // Make request + using (UnityWebRequest www = manager.Generate(resource, httpVerb)) { + byte[] raw_data = System.Text.Encoding.UTF8.GetBytes (Json.Serialize (data)); + www.uploadHandler = new UploadHandlerRaw (raw_data); + yield return www.Send (); + if (www.isError) { + Debug.LogError("Failed to Create Webhook: " + www.error); + } else { + // Parse Response + var webhookData = Json.Deserialize (www.downloadHandler.text) as Dictionary; + if (webhookData.ContainsKey ("message")) { + // Spark Error + error (new SparkMessage (webhookData)); + result (null); + } else { + // Create local Membership object + error(null); + result(new Webhook (webhookData)); + } + } + } + } + + /// + /// Delete this Webhook on Spark. + /// + public IEnumerator Delete(Action error, Action result) { + if (Id != null) { + var manager = GameObject.FindObjectOfType (); + using (UnityWebRequest www = manager.Generate ("webhooks/" + Id, UnityWebRequest.kHttpVerbDELETE)) { + yield return www.Send (); + if (www.isError) { + // Network Error + Debug.LogError ("Failed to Delete Webhook: " + www.error); + } else { + // Delete returns 204 on success + if (www.responseCode == 204) { + error (null); + result (true); + } else { + // Delete Failed + var json = Json.Deserialize (www.downloadHandler.text) as Dictionary; + error (new SparkMessage (json)); + result (false); + } + } + } + } + } + + public static IEnumerator ListWebhooks(Action error, Action> result, string teamId = null, int max = 0, string type = null) { + // Build Request + var manager = GameObject.FindObjectOfType (); + var data = new Dictionary (); + + // Handle optional arguments + if (teamId != null) { + data ["teamId"] = teamId; + } + if (max != 0) { + data ["max"] = max.ToString (); + } + if (type != null) { + data ["type"] = type; + } + + // Optional Parameters to URL query + string queryString = System.Text.Encoding.UTF8.GetString (UnityWebRequest.SerializeSimpleForm (data)); + + // Make Request + using (UnityWebRequest www = manager.Generate ("webhooks?" + queryString, UnityWebRequest.kHttpVerbGET)) { + yield return www.Send (); + + if (www.isError) { + // Network error + Debug.LogError("Failed to List Webhooks: " + www.error); + } else { + // Request succeeded, parse response + var json = Json.Deserialize (www.downloadHandler.text) as Dictionary; + + // Check for Spark side errors + if (json.ContainsKey ("message")) { + error (new SparkMessage (json)); + result (null); + } else { + // Convert to Membership objects + var webhooks = new List (); + var items = json ["items"] as List; + foreach (var webhook in items) { + webhooks.Add (new Webhook (webhook as Dictionary)); + } + result (webhooks); + error (null); + } + } + } + } + + /// + /// Gets the webhook details. + /// + /// The webhook details. + /// Webhook identifier. + /// Error from Spark if any. + /// Result Callback. + public static IEnumerator GetWebhookDetails(string webhookId, Action error, Action result) { + Request manager = GameObject.FindObjectOfType (); + using (UnityWebRequest www = manager.Generate ("webhooks/" + webhookId, UnityWebRequest.kHttpVerbGET)) { + yield return www.Send (); + + if (www.isError) { + // Network error + Debug.LogError (www.error); + } else { + // Parse Response + var webhookData = Json.Deserialize (www.downloadHandler.text) as Dictionary; + if (webhookData.ContainsKey ("message")) { + // Error Callback + error (new SparkMessage (webhookData)); + result(null); + } else { + // Result callback + error (null); + result(new Webhook (webhookData)); + } + } + } + } + } +} diff --git a/Spark SDK/Assets/Cisco/Spark SDK/Webhook.cs.meta b/SparkUnity/Assets/Cisco/Spark/Webhook.cs.meta similarity index 100% rename from Spark SDK/Assets/Cisco/Spark SDK/Webhook.cs.meta rename to SparkUnity/Assets/Cisco/Spark/Webhook.cs.meta diff --git a/Spark SDK/ProjectSettings/AudioManager.asset b/SparkUnity/ProjectSettings/AudioManager.asset similarity index 100% rename from Spark SDK/ProjectSettings/AudioManager.asset rename to SparkUnity/ProjectSettings/AudioManager.asset diff --git a/Spark SDK/ProjectSettings/ClusterInputManager.asset b/SparkUnity/ProjectSettings/ClusterInputManager.asset similarity index 100% rename from Spark SDK/ProjectSettings/ClusterInputManager.asset rename to SparkUnity/ProjectSettings/ClusterInputManager.asset diff --git a/Spark SDK/ProjectSettings/DynamicsManager.asset b/SparkUnity/ProjectSettings/DynamicsManager.asset similarity index 100% rename from Spark SDK/ProjectSettings/DynamicsManager.asset rename to SparkUnity/ProjectSettings/DynamicsManager.asset diff --git a/Spark SDK/ProjectSettings/EditorBuildSettings.asset b/SparkUnity/ProjectSettings/EditorBuildSettings.asset similarity index 100% rename from Spark SDK/ProjectSettings/EditorBuildSettings.asset rename to SparkUnity/ProjectSettings/EditorBuildSettings.asset diff --git a/Spark SDK/ProjectSettings/EditorSettings.asset b/SparkUnity/ProjectSettings/EditorSettings.asset similarity index 100% rename from Spark SDK/ProjectSettings/EditorSettings.asset rename to SparkUnity/ProjectSettings/EditorSettings.asset diff --git a/Spark SDK/ProjectSettings/GraphicsSettings.asset b/SparkUnity/ProjectSettings/GraphicsSettings.asset similarity index 100% rename from Spark SDK/ProjectSettings/GraphicsSettings.asset rename to SparkUnity/ProjectSettings/GraphicsSettings.asset diff --git a/Spark SDK/ProjectSettings/InputManager.asset b/SparkUnity/ProjectSettings/InputManager.asset similarity index 100% rename from Spark SDK/ProjectSettings/InputManager.asset rename to SparkUnity/ProjectSettings/InputManager.asset diff --git a/Spark SDK/ProjectSettings/NavMeshAreas.asset b/SparkUnity/ProjectSettings/NavMeshAreas.asset similarity index 100% rename from Spark SDK/ProjectSettings/NavMeshAreas.asset rename to SparkUnity/ProjectSettings/NavMeshAreas.asset diff --git a/Spark SDK/ProjectSettings/NetworkManager.asset b/SparkUnity/ProjectSettings/NetworkManager.asset similarity index 100% rename from Spark SDK/ProjectSettings/NetworkManager.asset rename to SparkUnity/ProjectSettings/NetworkManager.asset diff --git a/Spark SDK/ProjectSettings/Physics2DSettings.asset b/SparkUnity/ProjectSettings/Physics2DSettings.asset similarity index 100% rename from Spark SDK/ProjectSettings/Physics2DSettings.asset rename to SparkUnity/ProjectSettings/Physics2DSettings.asset diff --git a/Spark SDK/ProjectSettings/ProjectSettings.asset b/SparkUnity/ProjectSettings/ProjectSettings.asset similarity index 100% rename from Spark SDK/ProjectSettings/ProjectSettings.asset rename to SparkUnity/ProjectSettings/ProjectSettings.asset diff --git a/Spark SDK/ProjectSettings/ProjectVersion.txt b/SparkUnity/ProjectSettings/ProjectVersion.txt similarity index 100% rename from Spark SDK/ProjectSettings/ProjectVersion.txt rename to SparkUnity/ProjectSettings/ProjectVersion.txt diff --git a/Spark SDK/ProjectSettings/QualitySettings.asset b/SparkUnity/ProjectSettings/QualitySettings.asset similarity index 100% rename from Spark SDK/ProjectSettings/QualitySettings.asset rename to SparkUnity/ProjectSettings/QualitySettings.asset diff --git a/Spark SDK/ProjectSettings/TagManager.asset b/SparkUnity/ProjectSettings/TagManager.asset similarity index 100% rename from Spark SDK/ProjectSettings/TagManager.asset rename to SparkUnity/ProjectSettings/TagManager.asset diff --git a/Spark SDK/ProjectSettings/TimeManager.asset b/SparkUnity/ProjectSettings/TimeManager.asset similarity index 100% rename from Spark SDK/ProjectSettings/TimeManager.asset rename to SparkUnity/ProjectSettings/TimeManager.asset diff --git a/Spark SDK/ProjectSettings/UnityAdsSettings.asset b/SparkUnity/ProjectSettings/UnityAdsSettings.asset similarity index 100% rename from Spark SDK/ProjectSettings/UnityAdsSettings.asset rename to SparkUnity/ProjectSettings/UnityAdsSettings.asset diff --git a/Spark SDK/ProjectSettings/UnityConnectSettings.asset b/SparkUnity/ProjectSettings/UnityConnectSettings.asset similarity index 100% rename from Spark SDK/ProjectSettings/UnityConnectSettings.asset rename to SparkUnity/ProjectSettings/UnityConnectSettings.asset