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?

Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

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

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • 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

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on September 28, 2026, 11:44
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.