public async Task <TModel?> GetItemAsync <TModel>(Uri url, CmsApiOptions options)
            where TModel : class, IBaseContentItemModel
        {
            var apiDataModel = await apiDataProcessorService.GetAsync <TModel>(httpClient, url)
                               .ConfigureAwait(false);

            const int level = 0;
            var       retrievedContentTypes = new Dictionary <int, List <string> > {
                { level, new List <string> {
                      GetContentType(url.AbsolutePath),
                  } },
            };

            if (apiDataModel != null)
            {
                await GetSharedChildContentItems(
                    apiDataModel.ContentLinks,
                    apiDataModel.ContentItems,
                    retrievedContentTypes,
                    options,
                    level).ConfigureAwait(false);
            }

            return(apiDataModel);
        }
        private async Task GetSharedChildContentItems(
            ContentLinksModel?model,
            IList <IBaseContentItemModel> contentItem,
            Dictionary <int, List <string> > retrievedPaths,
            CmsApiOptions options,
            int level)
        {
            var filteredLinkDetails = model?.ContentLinks?
                                      .Where(x => !contentTypeMappingService.IgnoreRelationship.Any(z => z == x.Key));

            var linkDetails = filteredLinkDetails?
                              .SelectMany(contentLink => contentLink.Value)
                              .Where(x => !options.PreventRecursion || !AncestorsContainsType(x.Uri.AbsolutePath, retrievedPaths, level));

            if (linkDetails != null && linkDetails.Any())
            {
                if (!contentTypeMappingService.Mappings.Any())
                {
                    throw new InvalidOperationException($"No mappings have been added to {nameof(contentTypeMappingService)}. Please add mappings before calling {nameof(GetSharedChildContentItems)}");
                }

                foreach (var linkDetail in linkDetails)
                {
                    if (linkDetail.ContentType != null && linkDetail.ContentType.StartsWith("esco__", StringComparison.CurrentCultureIgnoreCase))
                    {
                        var newLink = linkDetail.Uri !.ToString().Replace("esco__", string.Empty, StringComparison.CurrentCultureIgnoreCase);
                        linkDetail.Uri = new Uri(newLink);
                    }

                    if (linkDetail.Uri != null)
                    {
                        await GetAndMapContentItem(contentItem, linkDetail, retrievedPaths, options, level + 1).ConfigureAwait(false);
                    }
                }
            }
        }
        private async Task GetAndMapContentItem(
            IList <IBaseContentItemModel> contentItem,
            ILinkDetails linkDetail,
            Dictionary <int, List <string> > retrievedPaths,
            CmsApiOptions options,
            int level)
        {
            var mappingToUse = contentTypeMappingService.GetMapping(linkDetail.ContentType !);

            var isOwnAncestor        = AncestorsContainsType(linkDetail.Uri !.AbsolutePath, retrievedPaths, level);
            var passedRecursionCheck = !options.PreventRecursion || !isOwnAncestor;

            if (mappingToUse == null || !passedRecursionCheck)
            {
                return;
            }

            if (!retrievedPaths.ContainsKey(level))
            {
                retrievedPaths.Add(level, new List <string>());
            }

            var contentType = GetContentType(linkDetail.Uri !.AbsolutePath) ?? "Unknown";

            retrievedPaths[level].Add(contentType);

            var keyName = (options.ContentTypeOptions?.ContainsKey(contentType) == true ?
                           options.ContentTypeOptions[contentType].KeyName : null) ?? "Uri";

            var key = keyName.ToUpperInvariant() switch
            {
                "TITLE" => linkDetail.Title !,
                _ => linkDetail.Uri.ToString()
            };

            if (options.ContentTypeOptions?.ContainsKey(contentType) == true &&
                options.ContentTypeOptions[contentType].Transform != null)
            {
                key = options.ContentTypeOptions[contentType].Transform !(key);
            }

            var pagesApiContentItemModel = GetFromApiCache <IBaseContentItemModel>(mappingToUse, key)
                                           ?? AddToApiCache(key, await GetContentItemAsync <IBaseContentItemModel>(mappingToUse !, linkDetail.Uri !).ConfigureAwait(false));

            if (pagesApiContentItemModel == null)
            {
                return;
            }

            mapper.Map(linkDetail, pagesApiContentItemModel);

            if (pagesApiContentItemModel.ContentLinks != null)
            {
                pagesApiContentItemModel.ContentLinks.ExcludePageLocation = true;

                await GetSharedChildContentItems(
                    pagesApiContentItemModel.ContentLinks,
                    pagesApiContentItemModel.ContentItems,
                    retrievedPaths,
                    options,
                    level).ConfigureAwait(false);
            }

            contentItem.Add(pagesApiContentItemModel !);
        }