< Summary

Class:DCLServices.WearablesCatalogService.LambdasWearablesCatalogService
Assembly:WearablesCatalogService
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLServices/WearablesCatalogService/LambdasWearablesCatalogService.cs
Covered lines:212
Uncovered lines:106
Coverable lines:318
Total lines:723
Line coverage:66.6% (212 of 318)
Covered branches:0
Total branches:0
Covered methods:25
Total methods:31
Method coverage:80.6% (25 of 31)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
WearableCollectionResponse()0%2100%
WearableCollectionResponse(...)0%110100%
LambdasWearablesCatalogService(...)0%110100%
Initialize()0%110100%
Dispose()0%110100%
GetThirdPartyCollectionsAsync()0%20400%
RequestOwnedWearablesAsync()0%22.520081.58%
RequestOwnedWearablesAsync()0%11.28063.16%
RequestBaseWearablesAsync()0%5.165081.25%
RequestThirdPartyWearablesByCollectionAsync()0%11.28063.16%
RequestWearableAsync()0%6.226081.82%
RequestWearableFromBuilderAsync()0%56700%
RequestWearableCollection()0%1101000%
RequestWearableCollectionInBuilder()0%1321100%
AddWearablesToCatalog(...)0%550100%
RemoveWearablesFromCatalog(...)0%330100%
RemoveWearableFromCatalog(...)0%110100%
RemoveWearablesInUse(...)0%550100%
AddEmbeddedWearablesToCatalog(...)0%4.054085.71%
Clear()0%110100%
IsValidWearable(...)0%220100%
DCLServices.Lambdas.ILambdaServiceConsumer<DCLServices.WearablesCatalogService.WearableWithDefinitionResponse>.CreateRequest(...)0%110100%
SyncWearablesRequestsAsync()0%24.3422083.08%
ValidateWearables(...)0%4.064084.62%
MapLambdasDataIntoWearableItem(...)0%10.069076.47%
IsInvalidWearable(...)0%7.96062.5%
IsInvalidWearable(...)0%10.667057.89%
IsLocalPreview()0%550100%
GetBuilderDomainUrl()0%6200%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLServices/WearablesCatalogService/LambdasWearablesCatalogService.cs

#LineLine coverage
 1using Cysharp.Threading.Tasks;
 2using DCL;
 3using DCL.Tasks;
 4using DCLServices.Lambdas;
 5using MainScripts.DCL.Helpers.Utils;
 6using System;
 7using System.Collections.Generic;
 8using System.Linq;
 9using System.Threading;
 10using UnityEngine;
 11using UnityEngine.Pool;
 12using static DCLServices.WearablesCatalogService.WearableWithEntityResponseDto.ElementDto;
 13
 14namespace DCLServices.WearablesCatalogService
 15{
 16    /// <summary>
 17    /// This service implements a direct way of getting wearables sending the requests directly to lambdas.
 18    /// </summary>
 19    public class LambdasWearablesCatalogService : IWearablesCatalogService, ILambdaServiceConsumer<WearableWithDefinitio
 20    {
 21        [Serializable]
 22        public class WearableRequest
 23        {
 24            public List<string> pointers;
 25        }
 26
 27        [Serializable]
 28        public class WearableCollectionResponse
 29        {
 30            public int total;
 31            public EntityDto[] entities;
 32
 033            public WearableCollectionResponse() { }
 34
 1435            public WearableCollectionResponse(EntityDto[] entities)
 36            {
 1437                this.entities = entities;
 1438            }
 39        }
 40
 44641        public BaseDictionary<string, WearableItem> WearablesCatalog { get; }
 42
 43        private const string PAGINATED_WEARABLES_END_POINT = "users/";
 44        private const string NON_PAGINATED_WEARABLES_END_POINT = "collections/wearables/";
 45        private const string THIRD_PARTY_COLLECTIONS_FETCH_URL = "third-party-integrations";
 46        private const int REQUESTS_TIME_OUT_SECONDS = 45;
 47        private const int MAX_WEARABLES_PER_REQUEST = 200;
 48
 49        private readonly ILambdasService lambdasService;
 12950        private readonly Dictionary<string, int> wearablesInUseCounters = new (new Dictionary<string, int>(), StringIgno
 12951        private readonly Dictionary<(string userId, int pageSize), LambdaResponsePagePointer<WearableWithDefinitionRespo
 12952        private readonly Dictionary<(string userId, string collectionId, int pageSize), LambdaResponsePagePointer<Wearab
 12953        private readonly List<string> pendingWearablesToRequest = new ();
 54        private readonly BaseVariable<FeatureFlag> featureFlags;
 55        private readonly DataStore dataStore;
 56        private readonly KernelConfig kernelConfig;
 57        private readonly ICatalyst catalyst;
 58
 1659        private string assetBundlesUrl => featureFlags.Get().IsFeatureEnabled("ab-new-cdn") ? "https://ab-cdn.decentrala
 60
 61        private CancellationTokenSource serviceCts;
 62        private UniTaskCompletionSource<IReadOnlyList<WearableItem>> lastRequestSource;
 63
 12964        public LambdasWearablesCatalogService(BaseDictionary<string, WearableItem> wearablesCatalog,
 65            ILambdasService lambdasService,
 66            IServiceProviders serviceProviders,
 67            BaseVariable<FeatureFlag> featureFlags,
 68            DataStore dataStore,
 69            KernelConfig kernelConfig)
 70        {
 12971            this.featureFlags = featureFlags;
 12972            this.dataStore = dataStore;
 12973            this.kernelConfig = kernelConfig;
 12974            this.lambdasService = lambdasService;
 12975            WearablesCatalog = wearablesCatalog;
 12976            catalyst = serviceProviders.catalyst;
 12977        }
 78
 79        public void Initialize()
 80        {
 5481            serviceCts = serviceCts.SafeRestart();
 5482        }
 83
 84        public void Dispose()
 85        {
 1486            serviceCts.SafeCancelAndDispose();
 1487            serviceCts = null;
 1488            Clear();
 1489        }
 90
 91        public async UniTask<WearableCollectionsAPIData.Collection[]> GetThirdPartyCollectionsAsync(CancellationToken ca
 92        {
 093            (WearableCollectionsAPIData response, bool success) = await lambdasService.Get<WearableCollectionsAPIData>(T
 94                THIRD_PARTY_COLLECTIONS_FETCH_URL, cancellationToken: cancellationToken);
 95
 096            if (!success)
 097                throw new Exception("Request error! third party collections couldn't be fetched!");
 98
 099            return response.data;
 0100        }
 101
 102        public async UniTask<(IReadOnlyList<WearableItem> wearables, int totalAmount)> RequestOwnedWearablesAsync(
 103            string userId, int pageNumber, int pageSize, CancellationToken cancellationToken, string category = null,
 104            NftRarity rarity = NftRarity.None,
 105            NftCollectionType collectionTypeMask = NftCollectionType.All,
 106            ICollection<string> thirdPartyCollectionIds = null,
 107            string name = null, (NftOrderByOperation type, bool directionAscendent)? orderBy = null)
 108        {
 4109            var queryParams = new List<(string name, string value)>
 110            {
 111                ("pageNum", pageNumber.ToString()),
 112                ("pageSize", pageSize.ToString()),
 113                ("includeEntities", "true"),
 114            };
 115
 4116            if (rarity != NftRarity.None)
 1117                queryParams.Add(("rarity", rarity.ToString().ToLower()));
 118
 4119            if (!string.IsNullOrEmpty(category))
 1120                queryParams.Add(("category", category));
 121
 4122            if (!string.IsNullOrEmpty(name))
 1123                queryParams.Add(("name", name));
 124
 4125            if (orderBy != null)
 126            {
 1127                queryParams.Add(("orderBy", orderBy.Value.type.ToString().ToLower()));
 1128                queryParams.Add(("direction", orderBy.Value.directionAscendent ? "ASC" : "DESC"));
 129            }
 130
 4131            if ((collectionTypeMask & NftCollectionType.Base) != 0)
 2132                queryParams.Add(("collectionType", "base-wearable"));
 133
 4134            if ((collectionTypeMask & NftCollectionType.OnChain) != 0)
 2135                queryParams.Add(("collectionType", "on-chain"));
 136
 4137            if ((collectionTypeMask & NftCollectionType.ThirdParty) != 0)
 2138                queryParams.Add(("collectionType", "third-party"));
 139
 4140            if (thirdPartyCollectionIds != null)
 4141                foreach (string collectionId in thirdPartyCollectionIds)
 1142                    queryParams.Add(("thirdPartyCollectionId", collectionId));
 143
 144            string explorerUrl;
 145            string contentUrl;
 146
 4147            if (IsLocalPreview())
 148            {
 0149                explorerUrl = "https://peer.decentraland.org/explorer/";
 0150                contentUrl = "https://peer.decentraland.org/content/contents/";
 151            }
 152            else
 153            {
 4154                string lambdasUrl = await catalyst.GetLambdaUrl(cancellationToken);
 4155                explorerUrl = lambdasUrl.Replace("/lambdas", "/explorer");
 4156                contentUrl = $"{catalyst.contentUrl}/contents/";
 157            }
 158
 4159            (WearableWithEntityResponseDto response, bool success) = await lambdasService.GetFromSpecificUrl<WearableWit
 160                $"{explorerUrl}/:userId/wearables",
 161                $"{explorerUrl}/{userId}/wearables",
 162                cancellationToken: cancellationToken,
 163                urlEncodedParams: queryParams.ToArray());
 164
 4165            if (!success)
 0166                throw new Exception($"The request of wearables for '{userId}' failed!");
 167
 4168            List<WearableItem> wearables = ValidateWearables(response.elements,
 169                contentUrl,
 170                assetBundlesUrl);
 171
 4172            AddWearablesToCatalog(wearables);
 173
 4174            return (wearables, response.TotalAmount);
 4175        }
 176
 177        public async UniTask<(IReadOnlyList<WearableItem> wearables, int totalAmount)> RequestOwnedWearablesAsync(string
 178        {
 1179            var createNewPointer = false;
 180
 2181            if (!ownerWearablesPagePointers.TryGetValue((userId, pageSize), out var pagePointer)) { createNewPointer = t
 0182            else if (cleanCachedPages)
 183            {
 0184                pagePointer.Dispose();
 0185                ownerWearablesPagePointers.Remove((userId, pageSize));
 0186                createNewPointer = true;
 187            }
 188
 1189            if (createNewPointer)
 190            {
 1191                ownerWearablesPagePointers[(userId, pageSize)] = pagePointer = new LambdaResponsePagePointer<WearableWit
 192                    $"{PAGINATED_WEARABLES_END_POINT}{userId}/wearables",
 193                    pageSize, ct, this);
 194            }
 195
 1196            var pageResponse = await pagePointer.GetPageAsync(pageNumber, ct);
 197
 1198            if (!pageResponse.success)
 0199                throw new Exception($"The request of the owned wearables for '{userId}' failed!");
 200
 3201            var wearables = pageResponse.response.elements.Select(x => x.definition).ToList();
 1202            MapLambdasDataIntoWearableItem(wearables);
 1203            AddWearablesToCatalog(wearables);
 204
 1205            return (wearables, pageResponse.response.TotalAmount);
 1206        }
 207
 208        public async UniTask RequestBaseWearablesAsync(CancellationToken ct)
 209        {
 1210            var url = $"{catalyst.contentUrl}entities/active/collections/{IWearablesCatalogService.BASE_WEARABLES_COLLEC
 211
 1212            var request = await lambdasService.GetFromSpecificUrl<WearableCollectionResponse>(url, url, cancellationToke
 213
 1214            if (!request.success)
 0215                throw new Exception("The request of the base wearables failed!");
 216
 1217            var poolList = PoolUtils.RentList<WearableItem>();
 1218            IList<WearableItem> wearableItems = poolList.GetList();
 219
 6220            foreach (EntityDto entityDto in request.response.entities)
 2221                wearableItems.Add(entityDto.ToWearableItem(catalyst.contentUrl, assetBundlesUrl, 1));
 222
 1223            MapLambdasDataIntoWearableItem(wearableItems);
 1224            AddWearablesToCatalog(wearableItems);
 225
 1226            poolList.Dispose();
 1227        }
 228
 229        public async UniTask<(IReadOnlyList<WearableItem> wearables, int totalAmount)> RequestThirdPartyWearablesByColle
 230            CancellationToken ct)
 231        {
 1232            var createNewPointer = false;
 233
 2234            if (!thirdPartyCollectionPagePointers.TryGetValue((userId, collectionId, pageSize), out var pagePointer)) { 
 0235            else if (cleanCachedPages)
 236            {
 0237                pagePointer.Dispose();
 0238                thirdPartyCollectionPagePointers.Remove((userId, collectionId, pageSize));
 0239                createNewPointer = true;
 240            }
 241
 1242            if (createNewPointer)
 243            {
 1244                thirdPartyCollectionPagePointers[(userId, collectionId, pageSize)] = pagePointer = new LambdaResponsePag
 245                    $"{PAGINATED_WEARABLES_END_POINT}{userId}/third-party-wearables/{collectionId}",
 246                    pageSize, ct, this);
 247            }
 248
 1249            var pageResponse = await pagePointer.GetPageAsync(pageNumber, ct);
 250
 1251            if (!pageResponse.success)
 0252                throw new Exception($"The request of the '{collectionId}' third party wearables collection of '{userId}'
 253
 3254            var wearables = pageResponse.response.elements.Select(x => x.definition).ToList();
 1255            MapLambdasDataIntoWearableItem(wearables);
 1256            AddWearablesToCatalog(wearables);
 257
 1258            return (wearables, pageResponse.response.TotalAmount);
 1259        }
 260
 261        public async UniTask<WearableItem> RequestWearableAsync(string wearableId, CancellationToken ct)
 262        {
 3263            if (WearablesCatalog.TryGetValue(wearableId, out WearableItem wearable))
 264            {
 1265                if (wearablesInUseCounters.ContainsKey(wearableId))
 1266                    wearablesInUseCounters[wearableId]++;
 267
 1268                return wearable;
 269            }
 270
 2271            ct.ThrowIfCancellationRequested();
 272
 273            try
 274            {
 275                // All the requests happened during the same frames interval are sent together
 6276                return await SyncWearablesRequestsAsync(wearableId, ct);
 277            }
 0278            catch (OperationCanceledException) { return null; }
 3279        }
 280
 281        public async UniTask<WearableItem> RequestWearableFromBuilderAsync(string wearableId, CancellationToken ct)
 282        {
 0283            if (WearablesCatalog.TryGetValue(wearableId, out WearableItem wearable))
 284            {
 0285                if (wearablesInUseCounters.ContainsKey(wearableId))
 0286                    wearablesInUseCounters[wearableId]++;
 287
 0288                return wearable;
 289            }
 290
 0291            string domain = GetBuilderDomainUrl();
 0292            var url = $"{domain}/items/{wearableId}";
 293
 0294            (WearableItemResponseFromBuilder response, bool success) = await lambdasService.GetFromSpecificUrl<WearableI
 295                domain, url,
 296                isSigned: true,
 297                cancellationToken: ct);
 298
 0299            if (!success)
 0300                throw new Exception($"The request of wearables from builder '{wearableId}' failed!");
 301
 0302            List<WearableItem> ws = new List<WearableItem>
 303            {
 304                response.data.ToWearableItem(
 305                    $"{domain}/storage/contents/",
 306                    assetBundlesUrl),
 307            };
 308
 0309            if (ws[0].IsEmote()) return null;
 310
 0311            AddWearablesToCatalog(ws);
 312
 0313            return ws[0];
 0314        }
 315
 316        public async UniTask<IReadOnlyList<WearableItem>> RequestWearableCollection(IEnumerable<string> collectionIds,
 317            CancellationToken cancellationToken, List<WearableItem> collectionBuffer = null)
 318        {
 0319            List<WearableItem> wearables = collectionBuffer ?? new List<WearableItem>();
 0320            var templateURL = $"{catalyst.contentUrl}entities/active/collections/:collectionId";
 321
 0322            foreach (string collectionId in collectionIds)
 323            {
 0324                string url = templateURL.Replace(":collectionId", collectionId);
 325
 0326                (WearableCollectionResponse response, bool success) = await lambdasService.GetFromSpecificUrl<WearableCo
 327                    templateURL, url,
 328                    cancellationToken: cancellationToken);
 329
 0330                if (!success)
 0331                    throw new Exception($"The request for collection of wearables '{collectionId}' failed!");
 332
 0333                var poolList = PoolUtils.RentList<WearableItem>();
 0334                IList<WearableItem> wearableItems = poolList.GetList();
 335
 0336                foreach (EntityDto entityDto in response.entities)
 0337                    wearableItems.Add(entityDto.ToWearableItem(catalyst.contentUrl, assetBundlesUrl, 1));
 338
 0339                MapLambdasDataIntoWearableItem(wearableItems);
 0340                AddWearablesToCatalog(wearableItems);
 341
 0342                wearables.AddRange(wearableItems);
 0343                poolList.Dispose();
 0344            }
 345
 0346            return wearables;
 0347        }
 348
 349        public async UniTask<(IReadOnlyList<WearableItem> wearables, int totalAmount)> RequestWearableCollectionInBuilde
 350            CancellationToken cancellationToken, List<WearableItem> collectionBuffer = null, string nameFilter = null,
 351            int pageNumber = 1, int pageSize = 5000)
 352        {
 0353            string domain = GetBuilderDomainUrl();
 0354            var wearables = collectionBuffer ?? new List<WearableItem>();
 355
 0356            var queryParams = new[]
 357            {
 358                ("page", pageNumber.ToString()),
 359                ("limit", pageSize.ToString()),
 360                ("name", nameFilter),
 361            };
 362
 0363            var totalAmount = 0;
 364
 0365            foreach (string collectionId in collectionIds)
 366            {
 0367                var url = $"{domain}/collections/{collectionId}/items/";
 0368                var templateUrl = $"{domain}/collections/:collectionId/items/";
 369
 0370                (WearableCollectionResponseFromBuilder response, bool success) = await lambdasService.GetFromSpecificUrl
 371                    templateUrl, url,
 372                    isSigned: true,
 373                    // urlEncodedParams: queryParams.Select(pair => (pair.Key, pair.Value)).ToArray(),
 374                    urlEncodedParams: queryParams,
 375                    cancellationToken: cancellationToken);
 376
 0377                if (!success)
 0378                    throw new Exception($"The request for collection of wearables from builder '{collectionId}' failed!"
 379
 0380                List<WearableItem> ws = response.data.results
 0381                                                .Select(bw => bw.ToWearableItem(
 382                                                     $"{domain}/storage/contents/",
 383                                                     assetBundlesUrl))
 0384                                                .Where(bw => !bw.IsEmote())
 385                                                .ToList();
 386
 0387                AddWearablesToCatalog(ws);
 388
 0389                wearables.AddRange(ws);
 0390                totalAmount = response.data.total;
 0391            }
 392
 0393            return (wearables, totalAmount);
 0394        }
 395
 396        public void AddWearablesToCatalog(IEnumerable<WearableItem> wearableItems)
 397        {
 62398            foreach (WearableItem wearableItem in wearableItems)
 399            {
 17400                if (WearablesCatalog.ContainsKey(wearableItem.id))
 401                    continue;
 402
 17403                wearableItem.SanitizeHidesLists();
 17404                WearablesCatalog.Add(wearableItem.id, wearableItem);
 405
 17406                if (!wearablesInUseCounters.ContainsKey(wearableItem.id))
 17407                    wearablesInUseCounters.Add(wearableItem.id, 1);
 408            }
 14409        }
 410
 411        public void RemoveWearablesFromCatalog(IEnumerable<string> wearableIds)
 412        {
 6413            foreach (string wearableId in wearableIds)
 2414                RemoveWearableFromCatalog(wearableId);
 1415        }
 416
 417        public void RemoveWearableFromCatalog(string wearableId)
 418        {
 2419            WearablesCatalog.Remove(wearableId);
 2420            wearablesInUseCounters.Remove(wearableId);
 2421        }
 422
 423        public void RemoveWearablesInUse(IEnumerable<string> wearablesInUseToRemove)
 424        {
 6425            foreach (string wearableToRemove in wearablesInUseToRemove)
 426            {
 2427                if (!wearablesInUseCounters.ContainsKey(wearableToRemove))
 428                    continue;
 429
 1430                wearablesInUseCounters[wearableToRemove]--;
 431
 1432                if (wearablesInUseCounters[wearableToRemove] > 0)
 433                    continue;
 434
 1435                WearablesCatalog.Remove(wearableToRemove);
 1436                wearablesInUseCounters.Remove(wearableToRemove);
 437            }
 1438        }
 439
 440        public void AddEmbeddedWearablesToCatalog(IEnumerable<WearableItem> wearables)
 441        {
 774442            foreach (WearableItem wearableItem in wearables)
 443            {
 375444                WearablesCatalog[wearableItem.id] = wearableItem;
 445
 375446                if (wearablesInUseCounters.ContainsKey(wearableItem.id))
 0447                    wearablesInUseCounters[wearableItem.id] = int.MaxValue; //A high value to ensure they are not remove
 448            }
 12449        }
 450
 451        public void Clear()
 452        {
 14453            WearablesCatalog.Clear();
 14454            wearablesInUseCounters.Clear();
 14455            pendingWearablesToRequest.Clear();
 14456            ownerWearablesPagePointers.Clear();
 14457            thirdPartyCollectionPagePointers.Clear();
 14458        }
 459
 460        public bool IsValidWearable(string wearableId)
 461        {
 4462            if (!WearablesCatalog.TryGetValue(wearableId, out var wearable))
 2463                return false;
 464
 2465            return wearable != null;
 466        }
 467
 468        UniTask<(WearableWithDefinitionResponse response, bool success)> ILambdaServiceConsumer<WearableWithDefinitionRe
 469            (string endPoint, int pageSize, int pageNumber, Dictionary<string, string> additionalData, CancellationToken
 2470            lambdasService.Get<WearableWithDefinitionResponse>(
 471                PAGINATED_WEARABLES_END_POINT,
 472                endPoint,
 473                REQUESTS_TIME_OUT_SECONDS,
 474                ILambdasService.DEFAULT_ATTEMPTS_NUMBER,
 475                cancellationToken,
 476                LambdaPaginatedResponseHelper.GetPageSizeParam(pageSize),
 477                LambdaPaginatedResponseHelper.GetPageNumParam(pageNumber),
 478                ("includeDefinitions", "true"));
 479
 480        private async UniTask<WearableItem> SyncWearablesRequestsAsync(string newWearableId, CancellationToken ct)
 481        {
 2482            pendingWearablesToRequest.Add(newWearableId);
 2483            lastRequestSource ??= new UniTaskCompletionSource<IReadOnlyList<WearableItem>>();
 2484            var sourceToAwait = lastRequestSource;
 485
 6486            await UniTask.Yield(PlayerLoopTiming.PostLateUpdate, cancellationToken: ct);
 487
 2488            List<WearableItem> result = new List<WearableItem>();
 489
 2490            if (pendingWearablesToRequest.Count > 0)
 491            {
 2492                lastRequestSource = null;
 493
 2494                using var wearableIdsPool = PoolUtils.RentList<string>();
 2495                var wearableIds = wearableIdsPool.GetList();
 2496                wearableIds.AddRange(pendingWearablesToRequest);
 2497                pendingWearablesToRequest.Clear();
 498
 499                // When the number of wearables to request is greater than MAX_WEARABLES_PER_REQUEST, we split the reque
 500                // In this way we avoid to send a very long url string that would fail due to the web request size limit
 2501                int numberOfPartialRequests = (wearableIds.Count + MAX_WEARABLES_PER_REQUEST - 1) / MAX_WEARABLES_PER_RE
 2502                var awaitingPartialTasksPool = PoolUtils.RentList<(UniTask<(EntityDto[] response, bool success)> task, I
 2503                var awaitingPartialTasks = awaitingPartialTasksPool.GetList();
 504
 8505                for (var i = 0; i < numberOfPartialRequests; i++)
 506                {
 2507                    int numberOfWearablesToRequest = wearableIds.Count < MAX_WEARABLES_PER_REQUEST
 508                        ? wearableIds.Count
 509                        : MAX_WEARABLES_PER_REQUEST;
 510
 2511                    var wearablesToRequest = new List<string>();
 2512                    int count = Math.Min(wearableIds.Count, numberOfWearablesToRequest);
 513
 8514                    for (int x = 0; x < count; x++)
 515                    {
 2516                        var urnAndTokenId = ExtendedUrnParser.GetShortenedUrn(wearableIds[x]);
 2517                        wearablesToRequest.Add(urnAndTokenId);
 518                    }
 519
 2520                    var request = new WearableRequest { pointers = wearablesToRequest };
 2521                    var url = $"{catalyst.contentUrl}entities/active";
 522
 2523                    var partialTask = lambdasService.PostFromSpecificUrl<EntityDto[], WearableRequest>(url, url, request
 524
 2525                    wearableIds.RemoveRange(0, numberOfWearablesToRequest);
 2526                    awaitingPartialTasks.Add((partialTask, wearablesToRequest));
 527                }
 528
 2529                var servicePartialResponsesPool = PoolUtils.RentList<((EntityDto[] response, bool success) taskResponse,
 2530                var servicePartialResponses = servicePartialResponsesPool.GetList();
 531
 532                try
 533                {
 8534                    foreach (var partialTask in awaitingPartialTasks)
 2535                        servicePartialResponses.Add((await partialTask.task, partialTask.wearablesRequested));
 2536                }
 0537                catch (Exception e)
 538                {
 0539                    sourceToAwait.TrySetException(e);
 0540                    throw;
 541                }
 542
 8543                foreach (var partialResponse in servicePartialResponses)
 544                {
 2545                    if (!partialResponse.taskResponse.success)
 546                    {
 0547                        Exception e = new Exception($"The request of the wearables ('{string.Join(", ", partialResponse.
 0548                        sourceToAwait.TrySetException(e);
 0549                        throw e;
 550                    }
 551
 2552                    var response = partialResponse.taskResponse.response;
 553
 2554                    string contentBaseUrl = $"{catalyst.contentUrl}contents/";
 555
 4556                    var wearables = response.Select(dto => dto.ToWearableItem(contentBaseUrl, assetBundlesUrl, 1)).ToLis
 557
 2558                    MapLambdasDataIntoWearableItem(wearables);
 2559                    AddWearablesToCatalog(wearables);
 2560                    result.AddRange(wearables);
 561                }
 562
 2563                sourceToAwait.TrySetResult(result);
 2564            }
 565            else
 0566                result = (List<WearableItem>)await sourceToAwait.Task;
 567
 2568            ct.ThrowIfCancellationRequested();
 569
 4570            return result.FirstOrDefault(x => string.Equals(x.id, ExtendedUrnParser.GetShortenedUrn(newWearableId),
 571                StringComparison.OrdinalIgnoreCase));
 2572        }
 573
 574        private List<WearableItem> ValidateWearables(
 575            IEnumerable<WearableWithEntityResponseDto.ElementDto> wearableElements,
 576            string contentBaseUrl,
 577            string bundlesBaseUrl)
 578        {
 4579            List<WearableItem> wearables = new ();
 580
 16581            foreach (var item in wearableElements)
 582            {
 4583                EntityDto entity = item.entity;
 4584                EntityDto.MetadataDto metadata = entity.metadata;
 585
 4586                if (IsInvalidWearable(metadata))
 587                    continue;
 588
 589                try
 590                {
 4591                    WearableItem wearable = item.ToWearableItem(contentBaseUrl, bundlesBaseUrl);
 4592                    wearables.Add(wearable);
 4593                }
 0594                catch (Exception e) { Debug.LogException(e); }
 595            }
 596
 4597            return wearables;
 598        }
 599
 600        private void MapLambdasDataIntoWearableItem(IList<WearableItem> wearablesFromLambdas)
 601        {
 5602            var invalidWearablesIndices = ListPool<int>.Get();
 603
 26604            for (var i = 0; i < wearablesFromLambdas.Count; i++)
 605            {
 8606                var wearable = wearablesFromLambdas[i];
 607
 8608                if (IsInvalidWearable(wearable))
 609                {
 0610                    invalidWearablesIndices.Add(i);
 0611                    continue;
 612                }
 613
 614                try
 615                {
 32616                    foreach (var representation in wearable.data.representations)
 617                    {
 32618                        foreach (var representationContent in representation.contents)
 8619                            representationContent.hash = representationContent.url[(representationContent.url.LastIndexO
 620                    }
 621
 8622                    string thumbnail = wearable.thumbnail ?? "";
 8623                    int index = thumbnail.LastIndexOf('/');
 8624                    string newThumbnail = thumbnail[(index + 1)..];
 8625                    string newBaseUrl = thumbnail[..(index + 1)];
 8626                    wearable.thumbnail = newThumbnail;
 8627                    wearable.baseUrl = string.IsNullOrEmpty(newBaseUrl) ? $"{catalyst.contentUrl}contents/" : newBaseUrl
 8628                    wearable.baseUrlBundles = assetBundlesUrl;
 8629                    wearable.emoteDataV0 = null;
 8630                }
 631                catch (Exception e)
 632                {
 0633                    Debug.LogException(e);
 0634                    invalidWearablesIndices.Add(i);
 0635                }
 636            }
 637
 10638            for (var i = 0; i < invalidWearablesIndices.Count; i++)
 639            {
 0640                int invalidWearablesIndex = invalidWearablesIndices[i] - i;
 0641                wearablesFromLambdas.RemoveAt(invalidWearablesIndex);
 642            }
 643
 5644            ListPool<int>.Release(invalidWearablesIndices);
 5645        }
 646
 647        private static bool IsInvalidWearable(WearableItem item)
 648        {
 8649            if (string.IsNullOrEmpty(item.id))
 650            {
 0651                Debug.LogError("Wearable is invalid: id is null");
 0652                return true;
 653            }
 654
 8655            if (item.data.representations == null)
 656            {
 0657                Debug.LogError($"Wearable ${item.id} is invalid: data.representation is null");
 0658                return true;
 659            }
 660
 32661            foreach (var dataRepresentation in item.data.representations)
 662            {
 32663                foreach (var representationContent in dataRepresentation.contents)
 664                {
 8665                    if (string.IsNullOrEmpty(representationContent.url))
 666                    {
 0667                        Debug.LogError("Wearable is invalid: representation content URL is null");
 0668                        return true;
 669                    }
 670                }
 671            }
 672
 8673            return false;
 674        }
 675
 676        private bool IsInvalidWearable(EntityDto.MetadataDto metadata)
 677        {
 4678            if (metadata == null)
 679            {
 0680                Debug.LogError("Wearable is invalid: metadata is null");
 0681                return true;
 682            }
 683
 4684            if (string.IsNullOrEmpty(metadata.id))
 685            {
 0686                Debug.LogError("Wearable is invalid: id is null");
 0687                return true;
 688            }
 689
 4690            if (metadata.data.representations == null)
 691            {
 0692                Debug.LogError($"Wearable ${metadata.id} is invalid: data.representation is null");
 0693                return true;
 694            }
 695
 16696            foreach (var representation in metadata.data.representations)
 697            {
 16698                foreach (string content in representation.contents)
 699                {
 4700                    if (string.IsNullOrEmpty(content))
 701                    {
 0702                        Debug.LogError("Wearable is invalid: representation content URL is null");
 0703                        return true;
 704                    }
 705                }
 706            }
 707
 4708            return false;
 709        }
 710
 711        private bool IsLocalPreview() =>
 4712            dataStore.realm.playerRealm.Get()?.serverName?.Equals("LocalPreview", StringComparison.OrdinalIgnoreCase) ??
 713
 714        private string GetBuilderDomainUrl()
 715        {
 0716            string domain = kernelConfig.Get().builderUrl;
 717
 0718            if (string.IsNullOrEmpty(domain))
 0719                domain = "https://builder-api.decentraland.org/v1";
 0720            return domain;
 721        }
 722    }
 723}

Methods/Properties

WearableCollectionResponse()
WearableCollectionResponse(DCLServices.WearablesCatalogService.WearableWithEntityResponseDto/ElementDto/EntityDto[])
WearablesCatalog()
LambdasWearablesCatalogService(.BaseDictionary[String,WearableItem], DCLServices.Lambdas.ILambdasService, DCL.IServiceProviders, .BaseVariable[FeatureFlag], DCL.DataStore, KernelConfig)
assetBundlesUrl()
Initialize()
Dispose()
GetThirdPartyCollectionsAsync()
RequestOwnedWearablesAsync()
RequestOwnedWearablesAsync()
RequestBaseWearablesAsync()
RequestThirdPartyWearablesByCollectionAsync()
RequestWearableAsync()
RequestWearableFromBuilderAsync()
RequestWearableCollection()
RequestWearableCollectionInBuilder()
AddWearablesToCatalog(System.Collections.Generic.IEnumerable[WearableItem])
RemoveWearablesFromCatalog(System.Collections.Generic.IEnumerable[String])
RemoveWearableFromCatalog(System.String)
RemoveWearablesInUse(System.Collections.Generic.IEnumerable[String])
AddEmbeddedWearablesToCatalog(System.Collections.Generic.IEnumerable[WearableItem])
Clear()
IsValidWearable(System.String)
DCLServices.Lambdas.ILambdaServiceConsumer<DCLServices.WearablesCatalogService.WearableWithDefinitionResponse>.CreateRequest(System.String, System.Int32, System.Int32, System.Collections.Generic.Dictionary[String,String], System.Threading.CancellationToken)
SyncWearablesRequestsAsync()
ValidateWearables(System.Collections.Generic.IEnumerable[ElementDto], System.String, System.String)
MapLambdasDataIntoWearableItem(System.Collections.Generic.IList[WearableItem])
IsInvalidWearable(WearableItem)
IsInvalidWearable(DCLServices.WearablesCatalogService.WearableWithEntityResponseDto/ElementDto/EntityDto/MetadataDto)
IsLocalPreview()
GetBuilderDomainUrl()