< Summary

Class:TheGraph
Assembly:TheGraph
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/ServiceProviders/TheGraph/TheGraph.cs
Covered lines:2
Uncovered lines:98
Coverable lines:100
Total lines:230
Line coverage:2% (2 of 100)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
TheGraph()0%110100%
Query(...)0%72800%
QueryLands(...)0%42600%
QueryPolygonMana(...)0%2100%
QueryNftCollections(...)0%2100%
QueryNftCollectionsByUrn(...)0%2100%
Dispose()0%110100%
GetSubGraphUrl(...)0%12300%
ProcessReceivedLandsData(...)0%12300%
ProcessReceivedNftData(...)0%12300%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/ServiceProviders/TheGraph/TheGraph.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Text;
 4using System.Text.RegularExpressions;
 5using DCL;
 6using DCL.Helpers;
 7using Newtonsoft.Json.Linq;
 8using UnityEngine;
 9using UnityEngine.Networking;
 10
 11public class TheGraph : ITheGraph
 12{
 13    private const float DEFAULT_CACHE_TIME = 5 * 60;
 14    private const string LAND_SUBGRAPH_URL_ORG = "https://api.thegraph.com/subgraphs/name/decentraland/land-manager";
 15    private const string LAND_SUBGRAPH_URL_ZONE = "https://api.thegraph.com/subgraphs/name/decentraland/land-manager-goe
 16    private const string LAND_SUBGRAPH_URL_MATIC = "https://api.thegraph.com/subgraphs/name/decentraland/mana-matic-main
 17    private const string NFT_COLLECTIONS_SUBGRAPH_URL_ETHEREUM = "https://api.thegraph.com/subgraphs/name/decentraland/c
 18    private const string NFT_COLLECTIONS_SUBGRAPH_URL_MATIC = "https://api.thegraph.com/subgraphs/name/decentraland/coll
 19
 12220    private readonly IDataCache<List<Land>> landQueryCache = new DataCache<List<Land>>();
 21
 22    private Promise<string> Query(string url, string query, QueryVariablesBase variables)
 23    {
 024        Promise<string> promise = new Promise<string>();
 25
 026        if (string.IsNullOrEmpty(query) || string.IsNullOrEmpty(url))
 27        {
 028            promise.Reject($"error: {(string.IsNullOrEmpty(url) ? "url" : "query")} is empty");
 029            return promise;
 30        }
 31
 032        string queryJson = $"\"query\":\"{Regex.Replace(query, @"\p{C}+", string.Empty)}\"";
 033        string variablesJson = variables != null ? $",\"variables\":{JsonUtility.ToJson(variables)}" : string.Empty;
 034        string bodyString = $"{{{queryJson}{variablesJson}}}";
 35
 036        var request = new UnityWebRequest();
 037        request.url = url;
 038        request.method = UnityWebRequest.kHttpVerbPOST;
 039        request.downloadHandler = new DownloadHandlerBuffer();
 040        request.uploadHandler = new UploadHandlerRaw(string.IsNullOrEmpty(bodyString) ? null : Encoding.UTF8.GetBytes(bo
 041        request.SetRequestHeader("Content-Type", "application/json");
 042        request.timeout = 60;
 43
 044        var operation = request.SendWebRequest();
 45
 046        operation.completed += asyncOperation =>
 47        {
 048            if (request.WebRequestSucceded())
 49            {
 050                promise.Resolve(request.downloadHandler.text);
 51            }
 52            else
 53            {
 054                promise.Reject($"error: {request.error} response: {request.downloadHandler.text}");
 55            }
 56
 057            request.Dispose();
 058        };
 59
 060        return promise;
 61    }
 62
 63    public Promise<List<Land>> QueryLands(string network, string address, float cacheMaxAgeSeconds)
 64    {
 065        string lowerCaseAddress = address.ToLower();
 66
 067        Promise<List<Land>> promise = new Promise<List<Land>>();
 68
 069        if (cacheMaxAgeSeconds >= 0)
 70        {
 071            if (landQueryCache.TryGet(lowerCaseAddress, out List<Land> cacheValue, out float lastUpdate))
 72            {
 073                if (Time.unscaledTime - lastUpdate <= cacheMaxAgeSeconds)
 74                {
 075                    promise.Resolve(cacheValue);
 076                    return promise;
 77                }
 78            }
 79        }
 80
 081        string url = network == "mainnet" ? LAND_SUBGRAPH_URL_ORG : LAND_SUBGRAPH_URL_ZONE;
 82
 083        Query(url, TheGraphQueries.getLandQuery, new AddressVariable() { address = lowerCaseAddress })
 84            .Then(resultJson =>
 85            {
 086                ProcessReceivedLandsData(promise, resultJson, lowerCaseAddress, true);
 087            })
 088            .Catch(error => promise.Reject(error));
 89
 090        return promise;
 91    }
 92
 93    public Promise<double> QueryPolygonMana(string address)
 94    {
 095        Promise<double> promise = new Promise<double>();
 96
 097        string lowerCaseAddress = address.ToLower();
 098        Query(LAND_SUBGRAPH_URL_MATIC, TheGraphQueries.getPolygonManaQuery, new AddressVariable() { address = lowerCaseA
 99            .Then(resultJson =>
 100            {
 101                try
 102                {
 0103                    JObject result = JObject.Parse(resultJson);
 0104                    JToken manaObject = result["data"]?["accounts"].First?["mana"];
 0105                    if (manaObject == null || !double.TryParse(manaObject.Value<string>(), out double parsedMana))
 0106                        throw new Exception($"QueryMana response couldn't be parsed: {resultJson}");
 107
 0108                    promise.Resolve(parsedMana / 1e18);
 0109                }
 0110                catch (Exception e)
 111                {
 0112                    promise.Reject(e.ToString());
 0113                }
 0114            })
 0115            .Catch(error => promise.Reject(error));
 0116        return promise;
 117    }
 118
 119    public Promise<List<Nft>> QueryNftCollections(string address, NftCollectionsLayer layer)
 120    {
 0121        Promise<List<Nft>> promise = new Promise<List<Nft>>();
 122
 0123        string url = GetSubGraphUrl(layer);
 124
 0125        Query(url, TheGraphQueries.getNftCollectionsQuery, new AddressVariable() { address = address.ToLower() })
 126            .Then(resultJson =>
 127            {
 0128                ProcessReceivedNftData(promise, resultJson);
 0129            })
 0130            .Catch(error => promise.Reject(error));
 131
 0132        return promise;
 133    }
 134
 135    public Promise<List<Nft>> QueryNftCollectionsByUrn(string address, string urn, NftCollectionsLayer layer)
 136    {
 0137        Promise<List<Nft>> promise = new Promise<List<Nft>>();
 138
 0139        string url = GetSubGraphUrl(layer);
 140
 0141        Query(url, TheGraphQueries.getNftCollectionByUserAndUrnQuery, new AddressAndUrnVariable() { address = address.To
 142            .Then(resultJson =>
 143            {
 0144                ProcessReceivedNftData(promise, resultJson);
 0145            })
 0146            .Catch(error => promise.Reject(error));
 147
 0148        return promise;
 149    }
 150
 244151    public void Dispose() { landQueryCache.Dispose(); }
 152
 153    private string GetSubGraphUrl(NftCollectionsLayer layer)
 154    {
 0155        string url = "";
 156        switch (layer)
 157        {
 158            case NftCollectionsLayer.ETHEREUM:
 0159                url = NFT_COLLECTIONS_SUBGRAPH_URL_ETHEREUM;
 0160                break;
 161            case NftCollectionsLayer.MATIC:
 0162                url = NFT_COLLECTIONS_SUBGRAPH_URL_MATIC;
 163                break;
 164        }
 165
 0166        return url;
 167    }
 168
 169    private void ProcessReceivedLandsData(Promise<List<Land>> landPromise, string jsonValue, string lowerCaseAddress, bo
 170    {
 0171        bool hasException = false;
 0172        List<Land> lands = null;
 173
 174        try
 175        {
 0176            LandQueryResultWrapped result = JsonUtility.FromJson<LandQueryResultWrapped>(jsonValue);
 0177            lands = LandHelper.ConvertQueryResult(result.data, lowerCaseAddress);
 178
 0179            if (cache)
 180            {
 0181                landQueryCache.Add(lowerCaseAddress, lands, DEFAULT_CACHE_TIME);
 182            }
 0183        }
 0184        catch (Exception exception)
 185        {
 0186            landPromise.Reject(exception.Message);
 0187            hasException = true;
 0188        }
 189        finally
 190        {
 0191            if (!hasException)
 192            {
 0193                landPromise.Resolve(lands);
 194            }
 0195        }
 0196    }
 197
 198    private void ProcessReceivedNftData(Promise<List<Nft>> nftPromise, string jsonValue)
 199    {
 0200        bool hasException = false;
 0201        List<Nft> nfts = new List<Nft>();
 202
 203        try
 204        {
 0205            NftQueryResultWrapped result = JsonUtility.FromJson<NftQueryResultWrapped>(jsonValue);
 206
 0207            foreach (var nft in result.data.nfts)
 208            {
 0209                nfts.Add(new Nft
 210                {
 211                    collectionId = nft.collection.id,
 212                    tokenId = nft.tokenId,
 213                    urn = nft.urn
 214                });
 215            }
 0216        }
 0217        catch (Exception exception)
 218        {
 0219            nftPromise.Reject(exception.Message);
 0220            hasException = true;
 0221        }
 222        finally
 223        {
 0224            if (!hasException)
 225            {
 0226                nftPromise.Resolve(nfts);
 227            }
 0228        }
 0229    }
 230}