Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs multi language #1180

Merged
merged 9 commits into from May 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion modules/docs/app/VoloDocs.Web/Pages/Index.cshtml.cs
Expand Up @@ -18,7 +18,7 @@ public IndexModel(IProjectAppService projectAppService)
_projectAppService = projectAppService;
}

public async Task<IActionResult> OnGet()
public async Task<IActionResult> OnGetAsync()
{
Projects = (await _projectAppService.GetListAsync()).Items;

Expand All @@ -28,6 +28,7 @@ public async Task<IActionResult> OnGet()
{
projectName = Projects[0].ShortName,
version = DocsAppConsts.Latest,
languageCode = await _projectAppService.GetDefaultLanguageCode(Projects[0].ShortName),
documentName = Projects[0].DefaultDocumentName
});
}
Expand Down
Expand Up @@ -25,6 +25,8 @@ public class DocumentWithDetailsDto

public string FileName { get; set; }

public string CurrentLanguageCode { get; set; }

public ProjectDto Project { get; set; }

public List<DocumentContributorDto> Contributors { get; set; }
Expand Down
@@ -1,5 +1,6 @@
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Docs.Language;
using Volo.Docs.Projects;

namespace Volo.Docs.Documents
Expand All @@ -10,5 +11,8 @@ public class GetDefaultDocumentInput

[StringLength(ProjectConsts.MaxVersionNameLength)]
public string Version { get; set; }

[StringLength(LanguageConsts.MaxLanguageCodeLength)]
public string LanguageCode { get; set; }
}
}
@@ -1,5 +1,6 @@
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Docs.Language;
using Volo.Docs.Projects;

namespace Volo.Docs.Documents
Expand All @@ -14,5 +15,8 @@ public class GetDocumentInput

[StringLength(ProjectConsts.MaxVersionNameLength)]
public string Version { get; set; }

[StringLength(LanguageConsts.MaxLanguageCodeLength)]
public string LanguageCode { get; set; }
}
}
@@ -1,5 +1,6 @@
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Docs.Language;
using Volo.Docs.Projects;

namespace Volo.Docs.Documents
Expand All @@ -14,5 +15,8 @@ public class GetDocumentResourceInput

[StringLength(ProjectConsts.MaxVersionNameLength)]
public string Version { get; set; }

[StringLength(LanguageConsts.MaxLanguageCodeLength)]
public string LanguageCode { get; set; }
}
}
@@ -1,5 +1,6 @@
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Docs.Language;
using Volo.Docs.Projects;

namespace Volo.Docs.Documents
Expand All @@ -10,5 +11,8 @@ public class GetNavigationDocumentInput

[StringLength(ProjectConsts.MaxVersionNameLength)]
public string Version { get; set; }

[StringLength(LanguageConsts.MaxLanguageCodeLength)]
public string LanguageCode { get; set; }
}
}
Expand Up @@ -9,6 +9,8 @@ public interface IProjectAppService : IApplicationService
Task<ListResultDto<ProjectDto>> GetListAsync();

Task<ProjectDto> GetAsync(string shortName);

Task<string> GetDefaultLanguageCode(string shortName);

Task<ListResultDto<VersionInfoDto>> GetVersionsAsync(string shortName);
}
Expand Down
Expand Up @@ -26,5 +26,7 @@ public class ProjectDto : EntityDto<Guid>
public string DocumentStoreType { get; set; }

public Dictionary<string, object> ExtraProperties { get; set; }

public Dictionary<string, string> Languages { get; set; }
}
}
Expand Up @@ -9,10 +9,10 @@ public class DocsApplicationAutoMapperProfile : Profile
{
public DocsApplicationAutoMapperProfile()
{
CreateMap<Project, ProjectDto>();
CreateMap<Project, ProjectDto>().Ignore(x=>x.Languages);
CreateMap<VersionInfo, VersionInfoDto>();
CreateMap<Document, DocumentWithDetailsDto>()
.Ignore(x => x.Project).Ignore(x => x.Contributors);
.Ignore(x => x.Project).Ignore(x => x.Contributors).Ignore(x => x.CurrentLanguageCode);
CreateMap<DocumentContributor, DocumentContributorDto>();
CreateMap<DocumentResource, DocumentResourceDto>();
}
Expand Down
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
Expand All @@ -15,27 +16,31 @@ public class DocumentAppService : ApplicationService, IDocumentAppService
private readonly IProjectRepository _projectRepository;
private readonly IDocumentStoreFactory _documentStoreFactory;
protected IDistributedCache<DocumentWithDetailsDto> DocumentCache { get; }
protected IDistributedCache<LanguageConfig> LanguageCache { get; }
protected IDistributedCache<DocumentResourceDto> ResourceCache { get; }

public DocumentAppService(
IProjectRepository projectRepository,
IDocumentStoreFactory documentStoreFactory,
IDistributedCache<DocumentWithDetailsDto> documentCache,
IDistributedCache<LanguageConfig> languageCache,
IDistributedCache<DocumentResourceDto> resourceCache)
{
_projectRepository = projectRepository;
_documentStoreFactory = documentStoreFactory;
DocumentCache = documentCache;
LanguageCache = languageCache;
ResourceCache = resourceCache;
}

public virtual async Task<DocumentWithDetailsDto> GetAsync(GetDocumentInput input)
{
var project = await _projectRepository.GetAsync(input.ProjectId);

return await GetDocumentWithDetailsDto(
return await GetDocument(
project,
input.Name,
input.LanguageCode,
input.Version
);
}
Expand All @@ -44,9 +49,10 @@ public virtual async Task<DocumentWithDetailsDto> GetDefaultAsync(GetDefaultDocu
{
var project = await _projectRepository.GetAsync(input.ProjectId);

return await GetDocumentWithDetailsDto(
return await GetDocument(
project,
project.DefaultDocumentName,
input.LanguageCode,
input.Version
);
}
Expand All @@ -55,22 +61,23 @@ public virtual async Task<DocumentWithDetailsDto> GetNavigationAsync(GetNavigati
{
var project = await _projectRepository.GetAsync(input.ProjectId);

return await GetDocumentWithDetailsDto(
return await GetDocument(
project,
project.NavigationDocumentName,
input.LanguageCode,
input.Version
);
}

public async Task<DocumentResourceDto> GetResourceAsync(GetDocumentResourceInput input)
{
var project = await _projectRepository.GetAsync(input.ProjectId);
var cacheKey = $"Resource@{project.ShortName}#{input.Name}#{input.Version}";
var cacheKey = $"Resource@{project.ShortName}#{input.LanguageCode}#{input.Name}#{input.Version}";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if input.LanguageCode is null?


async Task<DocumentResourceDto> GetResourceAsync()
{
var store = _documentStoreFactory.Create(project.DocumentStoreType);
var documentResource = await store.GetResource(project, input.Name, input.Version);
var documentResource = await store.GetResource(project, input.Name, input.LanguageCode, input.Version);

return ObjectMapper.Map<DocumentResource, DocumentResourceDto>(documentResource);
}
Expand All @@ -92,20 +99,23 @@ async Task<DocumentResourceDto> GetResourceAsync()
);
}

protected virtual async Task<DocumentWithDetailsDto> GetDocumentWithDetailsDto(
protected virtual async Task<DocumentWithDetailsDto> GetDocument(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename ro GetDocumentWithDetailsDtoAsync

Project project,
string documentName,
string languageCode,
string version)
{
var cacheKey = $"Document@{project.ShortName}#{documentName}#{version}";
var cacheKey = $"Document@{project.ShortName}#{languageCode}#{documentName}#{version}";

async Task<DocumentWithDetailsDto> GetDocumentAsync()
{
Logger.LogInformation($"Not found in the cache. Requesting {documentName} from the store...");
var store = _documentStoreFactory.Create(project.DocumentStoreType);
var document = await store.GetDocumentAsync(project, documentName, version);
var languages = await GetLanguageListAsync(store, project, version);
var language = GetLanguageByCode(languages, languageCode);
var document = await store.GetDocumentAsync(project, documentName, language.Code, version);
Logger.LogInformation($"Document retrieved: {documentName}");
return CreateDocumentWithDetailsDto(project, document);
return CreateDocumentWithDetailsDto(project, document, languages, language.Code);
}

if (Debugger.IsAttached)
Expand All @@ -125,11 +135,43 @@ async Task<DocumentWithDetailsDto> GetDocumentAsync()
);
}

protected virtual DocumentWithDetailsDto CreateDocumentWithDetailsDto(Project project, Document document)
protected virtual LanguageConfigElement GetLanguageByCode(LanguageConfig languageCodes, string languageCode)
{
var language = languageCodes.Languages.FirstOrDefault(l => l.Code == languageCode);

return language ?? languageCodes.Languages.Single(l => l.IsDefault);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throw a better exception if default language is not defined.

}

protected virtual async Task<LanguageConfig> GetLanguageListAsync(IDocumentStore store, Project project, string version)
{
async Task<LanguageConfig> GetLanguagesAsync()
{
return await store.GetLanguageListAsync(project, version);
}

return await LanguageCache.GetOrAddAsync(
project.ShortName,
GetLanguagesAsync,
() => new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(24)
}
);
}

protected virtual DocumentWithDetailsDto CreateDocumentWithDetailsDto(Project project, Document document, LanguageConfig languages, string languageCode)
{
var documentDto = ObjectMapper.Map<Document, DocumentWithDetailsDto>(document);
documentDto.Project = ObjectMapper.Map<Project, ProjectDto>(project);
documentDto.Contributors = ObjectMapper.Map<List<DocumentContributor>, List<DocumentContributorDto>>(document.Contributors);

documentDto.Project.Languages = new Dictionary<string, string>();
foreach (var language in languages.Languages)
{
documentDto.Project.Languages.Add(language.Code, language.DisplayName);
}

documentDto.CurrentLanguageCode = languageCode;
return documentDto;
}
}
Expand Down
Expand Up @@ -16,17 +16,15 @@ public class ProjectAppService : ApplicationService, IProjectAppService
private readonly IProjectRepository _projectRepository;
private readonly IDistributedCache<List<VersionInfo>> _versionCache;
private readonly IDocumentStoreFactory _documentStoreFactory;
private readonly IGuidGenerator _guidGenerator;

public ProjectAppService(
IProjectRepository projectRepository,
IDistributedCache<List<VersionInfo>> versionCache,
IDocumentStoreFactory documentStoreFactory, IGuidGenerator guidGenerator)
IDocumentStoreFactory documentStoreFactory)
{
_projectRepository = projectRepository;
_versionCache = versionCache;
_documentStoreFactory = documentStoreFactory;
_guidGenerator = guidGenerator;
}

public async Task<ListResultDto<ProjectDto>> GetListAsync()
Expand All @@ -45,6 +43,15 @@ public async Task<ProjectDto> GetAsync(string shortName)
return ObjectMapper.Map<Project, ProjectDto>(project);
}

public async Task<string> GetDefaultLanguageCode(string shortName)
{
var project = await _projectRepository.GetByShortNameAsync(shortName);
var store = _documentStoreFactory.Create(project.DocumentStoreType);
var languageList = await store.GetLanguageListAsync(project, project.LatestVersionBranchName);

return languageList.Languages.Single(l=>l.IsDefault).Code;
}

public async Task<ListResultDto<VersionInfoDto>> GetVersionsAsync(string shortName)
{
var project = await _projectRepository.GetByShortNameAsync(shortName);
Expand Down
@@ -0,0 +1,8 @@

namespace Volo.Docs.Language
{
public static class LanguageConsts
{
public const int MaxLanguageCodeLength = 10;
}
}
@@ -1,16 +1,19 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;
using Volo.Docs.GitHub.Documents;
using Volo.Docs.Projects;

namespace Volo.Docs.Documents
{
public interface IDocumentStore : IDomainService
{
Task<Document> GetDocumentAsync(Project project, string documentName, string version);
Task<Document> GetDocumentAsync(Project project, string documentName, string languageCode, string version);

Task<List<VersionInfo>> GetVersionsAsync(Project project);

Task<DocumentResource> GetResource(Project project, string resourceName, string version);
Task<DocumentResource> GetResource(Project project, string resourceName, string languageCode, string version);

Task<LanguageConfig> GetLanguageListAsync(Project project, string version);
}
}
@@ -0,0 +1,9 @@
using System.Collections.Generic;

namespace Volo.Docs.Documents
{
public class LanguageConfig
{
public List<LanguageConfigElement> Languages { get; set; }
}
}
@@ -0,0 +1,11 @@
namespace Volo.Docs.Documents
{
public class LanguageConfigElement
{
public string DisplayName { get; set; }

public string Code { get; set; }

public bool IsDefault { get; set; }
}
}