Open Closed

Update Cache For ABP Docs Module #9308


User avatar
0
Baytech created

Hello, we are currently integrating the Docs Module into our project. Is there a way to update how often the cache gets updated when fetching the documents from our Github Repository?


2 Answer(s)
  • User Avatar
    0
    enisn created
    Support Team .NET Developer

    Hi,

    I found where it's cached after repository is pulled: https://github.com/abpframework/abp/blob/b54f4bff04181fad94f1f27a0300eda37cfbc9f0/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs#L234

    Unfortunately it's not a virtual method that you cannot easily override it but you can override the methods that call this method such as PullAsync and PullAllAsync to define caching logic on your own:

    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(IDocumentAdminAppService), typeof(DocumentAdminAppService))]
    public class MyDocumentAdminAppService : DocumentAdminAppService
    {
        private readonly IProjectRepository _projectRepository;
        private readonly IDocumentRepository _documentRepository;
        private readonly IDocumentSourceFactory _documentStoreFactory;
        private readonly IDistributedCache<DocumentUpdateInfo> _documentUpdateCache;
        private readonly IDistributedCache<List<VersionInfo>> _versionCache;
        private readonly IDistributedCache<LanguageConfig> _languageCache;
        private readonly IDocumentFullSearch _elasticSearchService;
    
        public MyDocumentAdminAppService(IProjectRepository projectRepository, IDocumentRepository documentRepository, IDocumentSourceFactory documentStoreFactory, IDistributedCache<DocumentUpdateInfo> documentUpdateCache, IDistributedCache<List<VersionInfo>> versionCache, IDistributedCache<LanguageConfig> languageCache, IDocumentFullSearch elasticSearchService) : base(projectRepository, documentRepository, documentStoreFactory, documentUpdateCache, versionCache, languageCache, elasticSearchService)
        {
            this._projectRepository = projectRepository;
            this._documentRepository = documentRepository;
            this._documentStoreFactory = documentStoreFactory;
            this._documentUpdateCache = documentUpdateCache;
            this._versionCache = versionCache;
            this._languageCache = languageCache;
            this._elasticSearchService = elasticSearchService;
        }
    
        private async Task UpdateDocumentUpdateInfoCache(Document document)
        {
            var cacheKey = $"DocumentUpdateInfo{document.ProjectId}#{document.Name}#{document.LanguageCode}#{document.Version}";
            await _documentUpdateCache.SetAsync(cacheKey, new DocumentUpdateInfo
            {
                Name = document.Name,
                CreationTime = document.CreationTime,
                LastUpdatedTime = document.LastUpdatedTime
            },
    
            // 👇 Define your cache options here
            new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30),
                SlidingExpiration = TimeSpan.FromMinutes(10)
            });
        }
    
        /*
         * ====================
         * OVERRIDE OTHER METHODS WITH ORIGINAL LOGIC BELOW
         * THAT ENSURES THEY CALLS THE METHOD WE WROTE ABOVE
         * ====================
         */
    
        public override async Task PullAsync(PullDocumentInput input)
        {
            var project = await _projectRepository.GetAsync(input.ProjectId);
    
            var source = _documentStoreFactory.Create(project.DocumentStoreType);
            var sourceDocument = await source.GetDocumentAsync(project, input.Name, input.LanguageCode, input.Version);
    
            await _documentRepository.DeleteAsync(sourceDocument.ProjectId, sourceDocument.Name,
                sourceDocument.LanguageCode, sourceDocument.Version);
            await _documentRepository.InsertAsync(sourceDocument, true);
            await UpdateDocumentUpdateInfoCache(sourceDocument);
        }
    
        public override async Task PullAllAsync(PullAllDocumentInput input)
        {
            var project = await _projectRepository.GetAsync(input.ProjectId);
    
            var navigationDocument = await GetDocumentAsync(
                project,
                project.NavigationDocumentName,
                input.LanguageCode,
                input.Version
            );
    
            if (!DocsJsonSerializerHelper.TryDeserialize<NavigationNode>(navigationDocument.Content, out var navigation))
            {
                throw new UserFriendlyException($"Cannot validate navigation file '{project.NavigationDocumentName}' for the project {project.Name}.");
            }
    
            var leafs = navigation.Items.GetAllNodes(x => x.Items)
                .Where(x => x.IsLeaf && !x.Path.IsNullOrWhiteSpace())
                .ToList();
    
            var source = _documentStoreFactory.Create(project.DocumentStoreType);
    
            var documents = new List<Document>();
            foreach (var leaf in leafs)
            {
                if (leaf.Path.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
                    leaf.Path.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ||
                    (leaf.Path.StartsWith("{{") && leaf.Path.EndsWith("}}")))
                {
                    continue;
                }
    
                try
                {
                    var sourceDocument = await source.GetDocumentAsync(project, leaf.Path, input.LanguageCode, input.Version);
                    documents.Add(sourceDocument);
                }
                catch (Exception e)
                {
                    Logger.LogException(e);
                }
            }
    
            foreach (var document in documents)
            {
                await _documentRepository.DeleteAsync(document.ProjectId, document.Name,
                    document.LanguageCode,
                    document.Version);
    
                await _documentRepository.InsertAsync(document, true);
                await UpdateDocumentUpdateInfoCache(document);
            }
        }
    
        private async Task<Document> GetDocumentAsync(
                Project project,
                string documentName,
                string languageCode,
                string version)
        {
            version = string.IsNullOrWhiteSpace(version) ? project.LatestVersionBranchName : version;
            var source = _documentStoreFactory.Create(project.DocumentStoreType);
            var document = await source.GetDocumentAsync(project, documentName, languageCode, version);
            return document;
        }
    }
    

    It seems this module is not easily extended, I'll create an issue to the team to configure / customize it easily

  • User Avatar
    0
    enisn created
    Support Team .NET Developer

    And also this method for singular pull cachces: https://github.com/abpframework/abp/blob/d7e0ee30031eb61a59b0745c8fe81b94d4e859ef/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs#L408

Learn More, Pay Less
33% OFF
All Trainings!
Get Your Deal
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.0.0-preview. Updated on September 12, 2025, 10:20