< 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:235
Line coverage:2% (2 of 100)
Covered branches:0
Total branches:0
Covered methods:2
Total methods:10
Method coverage:20% (2 of 10)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
TheGraph()0%110100%
Query(...)0%72800%
QueryLands(...)0%42600%
QueryMana(...)0%30500%
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://subgraph.decentraland.org/land-manager";
 15    private const string LAND_SUBGRAPH_URL_ZONE = "https://api.studio.thegraph.com/query/49472/land-manager-sepolia/vers
 16    private const string MANA_SUBGRAPH_URL_ETHEREUM = "https://subgraph.decentraland.org/mana-ethereum-mainnet";
 17    private const string MANA_SUBGRAPH_URL_POLYGON = "https://subgraph.decentraland.org/mana-matic-mainnet";
 18    private const string NFT_COLLECTIONS_SUBGRAPH_URL_ETHEREUM = "https://subgraph.decentraland.org/collections-ethereum
 19    private const string NFT_COLLECTIONS_SUBGRAPH_URL_MATIC = "https://subgraph.decentraland.org/collections-matic-mainn
 20
 13021    private readonly IDataCache<List<Land>> landQueryCache = new DataCache<List<Land>>();
 22
 23    private Promise<string> Query(string url, string query, QueryVariablesBase variables)
 24    {
 025        Promise<string> promise = new Promise<string>();
 26
 027        if (string.IsNullOrEmpty(query) || string.IsNullOrEmpty(url))
 28        {
 029            promise.Reject($"error: {(string.IsNullOrEmpty(url) ? "url" : "query")} is empty");
 030            return promise;
 31        }
 32
 033        string queryJson = $"\"query\":\"{Regex.Replace(query, @"\p{C}+", string.Empty)}\"";
 034        string variablesJson = variables != null ? $",\"variables\":{JsonUtility.ToJson(variables)}" : string.Empty;
 035        string bodyString = $"{{{queryJson}{variablesJson}}}";
 36
 037        var request = new UnityWebRequest();
 038        request.url = url;
 039        request.method = UnityWebRequest.kHttpVerbPOST;
 040        request.downloadHandler = new DownloadHandlerBuffer();
 041        request.uploadHandler = new UploadHandlerRaw(string.IsNullOrEmpty(bodyString) ? null : Encoding.UTF8.GetBytes(bo
 042        request.SetRequestHeader("Content-Type", "application/json");
 043        request.timeout = 60;
 44
 045        var operation = request.SendWebRequest();
 46
 047        operation.completed += asyncOperation =>
 48        {
 049            if (request.WebRequestSucceded())
 50            {
 051                promise.Resolve(request.downloadHandler.text);
 52            }
 53            else
 54            {
 055                promise.Reject($"error: {request.error} response: {request.downloadHandler.text}");
 56            }
 57
 058            request.Dispose();
 059        };
 60
 061        return promise;
 62    }
 63
 64    public Promise<List<Land>> QueryLands(string network, string address, float cacheMaxAgeSeconds)
 65    {
 066        string lowerCaseAddress = address.ToLower();
 67
 068        Promise<List<Land>> promise = new Promise<List<Land>>();
 69
 070        if (cacheMaxAgeSeconds >= 0)
 71        {
 072            if (landQueryCache.TryGet(lowerCaseAddress, out List<Land> cacheValue, out float lastUpdate))
 73            {
 074                if (Time.unscaledTime - lastUpdate <= cacheMaxAgeSeconds)
 75                {
 076                    promise.Resolve(cacheValue);
 077                    return promise;
 78                }
 79            }
 80        }
 81
 082        string url = network == "mainnet" ? LAND_SUBGRAPH_URL_ORG : LAND_SUBGRAPH_URL_ZONE;
 83
 084        Query(url, TheGraphQueries.getLandQuery, new AddressVariable() { address = lowerCaseAddress })
 85            .Then(resultJson =>
 86            {
 087                ProcessReceivedLandsData(promise, resultJson, lowerCaseAddress, true);
 088            })
 089            .Catch(error => promise.Reject(error));
 90
 091        return promise;
 92    }
 93
 94    public Promise<double> QueryMana(string address, TheGraphNetwork network)
 95    {
 096        Promise<double> promise = new Promise<double>();
 97
 098        string lowerCaseAddress = address.ToLower();
 099        Query(
 100                network == TheGraphNetwork.Ethereum ? MANA_SUBGRAPH_URL_ETHEREUM : MANA_SUBGRAPH_URL_POLYGON,
 101                network == TheGraphNetwork.Ethereum ? TheGraphQueries.getEthereumManaQuery : TheGraphQueries.getPolygonM
 102                new AddressVariable() { address = lowerCaseAddress })
 103           .Then(resultJson =>
 104            {
 105                try
 106                {
 0107                    JObject result = JObject.Parse(resultJson);
 0108                    JToken manaObject = result["data"]?["accounts"].First?["mana"];
 0109                    if (manaObject == null || !double.TryParse(manaObject.Value<string>(), out double parsedMana))
 0110                        throw new Exception($"QueryMana response couldn't be parsed: {resultJson}");
 111
 0112                    promise.Resolve(parsedMana / 1e18);
 0113                }
 0114                catch (Exception e)
 115                {
 0116                    promise.Reject(e.ToString());
 0117                }
 0118            })
 0119           .Catch(error => promise.Reject(error));
 0120        return promise;
 121    }
 122
 123    public Promise<List<Nft>> QueryNftCollections(string address, NftCollectionsLayer layer)
 124    {
 0125        Promise<List<Nft>> promise = new Promise<List<Nft>>();
 126
 0127        string url = GetSubGraphUrl(layer);
 128
 0129        Query(url, TheGraphQueries.getNftCollectionsQuery, new AddressVariable() { address = address.ToLower() })
 130            .Then(resultJson =>
 131            {
 0132                ProcessReceivedNftData(promise, resultJson);
 0133            })
 0134            .Catch(error => promise.Reject(error));
 135
 0136        return promise;
 137    }
 138
 139    public Promise<List<Nft>> QueryNftCollectionsByUrn(string address, string urn, NftCollectionsLayer layer)
 140    {
 0141        Promise<List<Nft>> promise = new Promise<List<Nft>>();
 142
 0143        string url = GetSubGraphUrl(layer);
 144
 0145        Query(url, TheGraphQueries.getNftCollectionByUserAndUrnQuery, new AddressAndUrnVariable() { address = address.To
 146            .Then(resultJson =>
 147            {
 0148                ProcessReceivedNftData(promise, resultJson);
 0149            })
 0150            .Catch(error => promise.Reject(error));
 151
 0152        return promise;
 153    }
 154
 260155    public void Dispose() { landQueryCache.Dispose(); }
 156
 157    private string GetSubGraphUrl(NftCollectionsLayer layer)
 158    {
 0159        string url = "";
 160        switch (layer)
 161        {
 162            case NftCollectionsLayer.ETHEREUM:
 0163                url = NFT_COLLECTIONS_SUBGRAPH_URL_ETHEREUM;
 0164                break;
 165            case NftCollectionsLayer.MATIC:
 0166                url = NFT_COLLECTIONS_SUBGRAPH_URL_MATIC;
 167                break;
 168        }
 169
 0170        return url;
 171    }
 172
 173    private void ProcessReceivedLandsData(Promise<List<Land>> landPromise, string jsonValue, string lowerCaseAddress, bo
 174    {
 0175        bool hasException = false;
 0176        List<Land> lands = null;
 177
 178        try
 179        {
 0180            LandQueryResultWrapped result = JsonUtility.FromJson<LandQueryResultWrapped>(jsonValue);
 0181            lands = LandHelper.ConvertQueryResult(result.data, lowerCaseAddress);
 182
 0183            if (cache)
 184            {
 0185                landQueryCache.Add(lowerCaseAddress, lands, DEFAULT_CACHE_TIME);
 186            }
 0187        }
 0188        catch (Exception exception)
 189        {
 0190            landPromise.Reject(exception.Message);
 0191            hasException = true;
 0192        }
 193        finally
 194        {
 0195            if (!hasException)
 196            {
 0197                landPromise.Resolve(lands);
 198            }
 0199        }
 0200    }
 201
 202    private void ProcessReceivedNftData(Promise<List<Nft>> nftPromise, string jsonValue)
 203    {
 0204        bool hasException = false;
 0205        List<Nft> nfts = new List<Nft>();
 206
 207        try
 208        {
 0209            NftQueryResultWrapped result = JsonUtility.FromJson<NftQueryResultWrapped>(jsonValue);
 210
 0211            foreach (var nft in result.data.nfts)
 212            {
 0213                nfts.Add(new Nft
 214                {
 215                    collectionId = nft.collection.id,
 216                    blockchainId =  nft.item.blockchainId,
 217                    tokenId = nft.tokenId,
 218                    urn = nft.urn,
 219                });
 220            }
 0221        }
 0222        catch (Exception exception)
 223        {
 0224            nftPromise.Reject(exception.Message);
 0225            hasException = true;
 0226        }
 227        finally
 228        {
 0229            if (!hasException)
 230            {
 0231                nftPromise.Resolve(nfts);
 232            }
 0233        }
 0234    }
 235}