ContentfulManager.cs 8.91 KB
using Contentful.Core;
using Contentful.Core.Configuration;
using Contentful.Core.Models;
using Contentful.Core.Search;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using UnityEngine;
using UnityEngine.Networking;

namespace Contentful.Globacore
{
    public enum RequestType
    {
        FETCH,
        CREATE
    };

    public class Request
    {
        public Action<Response> onComplete { get; set; }
        
        public object data { get; set; }

        public string contentType { get; set; }

        public RequestType requestType { get; set; } = RequestType.FETCH;

        public string searchField { get; set; }

        public string query { get; set; }

        /// <summary>
        /// Send request to Contentful DB. Make sure to pass to initialization data to the request object manually before calling this
        /// </summary>
        public void Send()
        {
            ContentfulManager.Instance.Create(this);
        }

        /// <summary>
        /// Send request to Contentful DB. Asusumes the Contentful ContentType name is the same as the name of the class being passed
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void Send<T>()
        {
            contentType = FormatContentType(typeof(T).ToString());
            ContentfulManager.Instance.Create(this);
        }

        public void Send<T>(string contentType)
        {
            this.contentType = contentType;
            ContentfulManager.Instance.Create(this);
        }

        /// <summary>
        /// Send request to post content to Contentful DB
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <param name="contentType"></param>
        public void Send<T>(T obj, string contentType)
        {
            requestType = RequestType.CREATE;
            data = obj;
            this.contentType = contentType;
            ContentfulManager.Instance.Create(this);
        }

        /// <summary>
        /// Send request to post content to Contentful DB. Asusumes the Contentful ContentType name is the same as the name of the class being passed
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <param name="contentType"></param>
        public void Send<T>(T obj)
        {
            contentType = FormatContentType(typeof(T).ToString());

            requestType = RequestType.CREATE;
            data = obj;

            ContentfulManager.Instance.Create(this);
        }

        public void Send<T>(string searchField, string query)
        {
            contentType = FormatContentType(typeof(T).ToString());

            this.searchField = searchField;
            this.query = query;

            ContentfulManager.Instance.Create(this);
        }

        private string FormatContentType(string txt)
        {
            return (System.Char.ToLowerInvariant(txt[0]) + txt.Substring(1));
        }
    }

    public class Response
    {
        public Request request { get; set; }

        public ContentfulCollection<object> rawResponse { get; set; }

        /// <summary>
        /// Cast response from Contentful as type List<T>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public List<T> GetAll<T>()
        {
            var result = new List<T>();
            foreach(var item in rawResponse)
            {
                var obj = (Newtonsoft.Json.Linq.JObject)item;
                result.Add(obj.ToObject<T>());
            }
            return result;
        }

        /// <summary>
        /// Get first result from Contentful response as type T
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T GetFirst<T>()
        {
            var first = (Newtonsoft.Json.Linq.JObject) rawResponse.FirstOrDefault();
            var obj = first.ToObject<T>();

            return obj;
        }
    }

    public class ContentfulManager : Singleton<ContentfulManager>
    {
        #region Init

        [SerializeField]
        private string _spaceId, _deliveryToken, _managementToken;

        private ContentfulClient _client;
        private ContentfulManagementClient _mClient;

        private void Awake()
        {
            ContentfulOptions options = new ContentfulOptions()
            {
                ManagementApiKey = _managementToken,
                DeliveryApiKey = _deliveryToken,
                SpaceId = _spaceId
            };

            _client = new ContentfulClient(new HttpClient(), options);
            _mClient = new ContentfulManagementClient(new HttpClient(), options);

            StartCoroutine(JobQueue());
        }

        public void Create(Request request)
        {
            _activeRequests.Enqueue(request);
        }

        private Queue<Request> _activeRequests = new Queue<Request>();

        private IEnumerator JobQueue()
        {
            while (true)
            {
                while (_activeRequests.Count == 0) yield return null;

                var thisReq = _activeRequests.Dequeue();

                Coroutine reqCoroutine = null;

                switch (thisReq.requestType)
                {
                    case RequestType.FETCH:
                        reqCoroutine = StartCoroutine(FetchRequest(thisReq));
                        break;
                    case RequestType.CREATE:
                        reqCoroutine = StartCoroutine(CreateRequest(thisReq));
                        break;
                }

                yield return reqCoroutine;
            }
        }

        #endregion

        private IEnumerator FetchRequest(Request req)
        {
            string search = "fields." + req.searchField;

            var queryBuilder = new QueryBuilder<object>().ContentTypeIs(req.contentType).FieldEquals(search, req.query).Include(10);
            var fetchReq = _client.GetEntries(queryBuilder);

            while (!fetchReq.IsCompleted) yield return null;

            Response thisResp = new Response()
            {
                request = req,
                rawResponse = fetchReq.Result
            };

            req.onComplete?.Invoke(thisResp);
        }

        private IEnumerator CreateRequest(Request req)
        {
            var entry = GenerateEntry(req.data, req.GetType().ToString());
            
            var createEntry = _mClient.CreateEntry(entry, contentTypeId: req.contentType);
            while (!createEntry.IsCompleted) yield return null;

            print(createEntry.Result.GetType());
            print(Newtonsoft.Json.JsonConvert.SerializeObject(createEntry));
            var createdEntry = createEntry.Result;

            var publishEntry = _mClient.PublishEntry(createdEntry.SystemProperties.Id, 1);
            while (!publishEntry.IsCompleted) yield return null;

            Debug.Log(publishEntry.Result.ToString());
            Response thisResp = new Response()
            {
                request = req,
                //rawResponse = publishEntry.Result
            };

            req.onComplete?.Invoke(thisResp);
        }

        private Entry<dynamic> GenerateEntry<T>(T obj, string contentType)
        {
            Type type = obj.GetType();

            var entry = new Entry<dynamic>();
            entry.SystemProperties = new SystemProperties();
            entry.SystemProperties.Id = contentType;

            var dict = new Dictionary<string, object>();

            foreach (var prop in type.GetProperties())
            {
                if (prop.PropertyType == typeof(SystemProperties)) continue;

                // ignore what ought be ignored
                if (Attribute.IsDefined(prop, typeof(Newtonsoft.Json.JsonIgnoreAttribute))) continue;

                dict.Add(prop.Name, new Dictionary<string, object>()
                {
                    { "en-US", prop.GetValue(obj) }
                });
            }

            foreach (var prop in type.GetFields())
            {
                if (prop.FieldType == typeof(SystemProperties)) continue;

                // ignore what ought be ignored
                if (Attribute.IsDefined(prop, typeof(Newtonsoft.Json.JsonIgnoreAttribute))) continue;

                dict.Add(prop.Name, new Dictionary<string, object>()
                {
                    { "en-US", prop.GetValue(obj) }
                });
            }

            entry.Fields = dict;

            return entry;
        }

        public void GetAsset(string id, string url, Action<byte[]> onComplete)
        {
            StartCoroutine(GetAssetCoroutine(id, url, onComplete));
        }

        private IEnumerator GetAssetCoroutine(string id, string url, Action<byte[]> onComplete)
        {
            Debug.Log(url);
            var www = UnityWebRequest.Get(url);

            yield return www.SendWebRequest();

            onComplete?.Invoke(www.downloadHandler.data);
        }
    }
}