mirror of https://github.com/abpframework/abp.git
34 changed files with 444 additions and 104 deletions
@ -0,0 +1,8 @@ |
|||
# ABP Version 7.4 Migration Guide |
|||
|
|||
This document is a guide for upgrading ABP v7.3 solutions to ABP v7.4. There are a few changes in this version that may affect your applications, please read it carefully and apply the necessary changes to your application. |
|||
|
|||
## TemplateDefinition |
|||
|
|||
The `LocalizationResource(Type)` of `TemplateDefinition` class is changed to `LocalizationResourceName(string)`. |
|||
|
|||
@ -0,0 +1,14 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.TextTemplating; |
|||
|
|||
public interface IDynamicTemplateDefinitionStore |
|||
{ |
|||
Task<TemplateDefinition> GetAsync([NotNull] string name); |
|||
|
|||
Task<IReadOnlyList<TemplateDefinition>> GetAllAsync(); |
|||
|
|||
Task<TemplateDefinition> GetOrNullAsync(string name); |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.TextTemplating; |
|||
|
|||
public interface IStaticTemplateDefinitionStore |
|||
{ |
|||
Task<TemplateDefinition> GetAsync([NotNull] string name); |
|||
|
|||
Task<IReadOnlyList<TemplateDefinition>> GetAllAsync(); |
|||
|
|||
Task<TemplateDefinition> GetOrNullAsync(string name); |
|||
} |
|||
@ -1,16 +1,17 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.TextTemplating; |
|||
|
|||
public interface ITemplateDefinitionManager |
|||
{ |
|||
[NotNull] |
|||
TemplateDefinition Get([NotNull] string name); |
|||
[ItemNotNull] |
|||
Task<TemplateDefinition> GetAsync([NotNull] string name); |
|||
|
|||
[NotNull] |
|||
IReadOnlyList<TemplateDefinition> GetAll(); |
|||
[ItemNotNull] |
|||
Task<IReadOnlyList<TemplateDefinition>> GetAllAsync(); |
|||
|
|||
[CanBeNull] |
|||
TemplateDefinition GetOrNull(string name); |
|||
[ItemCanBeNull] |
|||
Task<TemplateDefinition> GetOrNullAsync(string name); |
|||
} |
|||
|
|||
@ -0,0 +1,29 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.TextTemplating; |
|||
|
|||
public class NullIDynamicTemplateDefinitionStore : IDynamicTemplateDefinitionStore, ISingletonDependency |
|||
{ |
|||
private readonly static Task<TemplateDefinition> CachedTemplateResult = Task.FromResult((TemplateDefinition)null); |
|||
|
|||
private readonly static Task<IReadOnlyList<TemplateDefinition>> CachedFeaturesResult = Task.FromResult((IReadOnlyList<TemplateDefinition>)Array.Empty<TemplateDefinition>().ToImmutableList()); |
|||
|
|||
public Task<TemplateDefinition> GetAsync(string name) |
|||
{ |
|||
return CachedTemplateResult; |
|||
} |
|||
|
|||
public Task<IReadOnlyList<TemplateDefinition>> GetAllAsync() |
|||
{ |
|||
return CachedFeaturesResult; |
|||
} |
|||
|
|||
public Task<TemplateDefinition> GetOrNullAsync(string name) |
|||
{ |
|||
return CachedTemplateResult; |
|||
} |
|||
} |
|||
@ -0,0 +1,83 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.TextTemplating; |
|||
|
|||
public class StaticTemplateDefinitionStore : IStaticTemplateDefinitionStore, ISingletonDependency |
|||
{ |
|||
protected Lazy<IDictionary<string, TemplateDefinition>> TemplateDefinitions { get; } |
|||
|
|||
protected AbpTextTemplatingOptions Options { get; } |
|||
|
|||
protected IServiceProvider ServiceProvider { get; } |
|||
|
|||
public StaticTemplateDefinitionStore(IOptions<AbpTextTemplatingOptions> options, IServiceProvider serviceProvider) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
Options = options.Value; |
|||
|
|||
TemplateDefinitions = new Lazy<IDictionary<string, TemplateDefinition>>(CreateTextTemplateDefinitions, true); |
|||
} |
|||
|
|||
public virtual Task<TemplateDefinition> GetAsync(string name) |
|||
{ |
|||
Check.NotNull(name, nameof(name)); |
|||
|
|||
var template = GetOrNullAsync(name); |
|||
|
|||
if (template == null) |
|||
{ |
|||
throw new AbpException("Undefined template: " + name); |
|||
} |
|||
|
|||
return template; |
|||
} |
|||
|
|||
public virtual Task<IReadOnlyList<TemplateDefinition>> GetAllAsync() |
|||
{ |
|||
return Task.FromResult<IReadOnlyList<TemplateDefinition>>(TemplateDefinitions.Value.Values.ToImmutableList()); |
|||
} |
|||
|
|||
public virtual Task<TemplateDefinition> GetOrNullAsync(string name) |
|||
{ |
|||
return Task.FromResult(TemplateDefinitions.Value.GetOrDefault(name)); |
|||
} |
|||
|
|||
protected virtual IDictionary<string, TemplateDefinition> CreateTextTemplateDefinitions() |
|||
{ |
|||
var templates = new Dictionary<string, TemplateDefinition>(); |
|||
|
|||
using (var scope = ServiceProvider.CreateScope()) |
|||
{ |
|||
var providers = Options |
|||
.DefinitionProviders |
|||
.Select(p => scope.ServiceProvider.GetRequiredService(p) as ITemplateDefinitionProvider) |
|||
.ToList(); |
|||
|
|||
var context = new TemplateDefinitionContext(templates); |
|||
|
|||
foreach (var provider in providers) |
|||
{ |
|||
provider.PreDefine(context); |
|||
} |
|||
|
|||
foreach (var provider in providers) |
|||
{ |
|||
provider.Define(context); |
|||
} |
|||
|
|||
foreach (var provider in providers) |
|||
{ |
|||
provider.PostDefine(context); |
|||
} |
|||
} |
|||
|
|||
return templates; |
|||
} |
|||
} |
|||
@ -1,85 +1,50 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using System.Linq; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.TextTemplating; |
|||
|
|||
public class TemplateDefinitionManager : ITemplateDefinitionManager, ISingletonDependency |
|||
{ |
|||
protected Lazy<IDictionary<string, TemplateDefinition>> TemplateDefinitions { get; } |
|||
protected readonly IStaticTemplateDefinitionStore StaticStore; |
|||
protected readonly IDynamicTemplateDefinitionStore DynamicStore; |
|||
|
|||
protected AbpTextTemplatingOptions Options { get; } |
|||
|
|||
protected IServiceProvider ServiceProvider { get; } |
|||
|
|||
public TemplateDefinitionManager( |
|||
IOptions<AbpTextTemplatingOptions> options, |
|||
IServiceProvider serviceProvider) |
|||
public TemplateDefinitionManager(IStaticTemplateDefinitionStore staticStore, IDynamicTemplateDefinitionStore dynamicStore) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
Options = options.Value; |
|||
|
|||
TemplateDefinitions = |
|||
new Lazy<IDictionary<string, TemplateDefinition>>(CreateTextTemplateDefinitions, true); |
|||
StaticStore = staticStore; |
|||
DynamicStore = dynamicStore; |
|||
} |
|||
|
|||
public virtual TemplateDefinition Get(string name) |
|||
public virtual async Task<TemplateDefinition> GetAsync(string name) |
|||
{ |
|||
Check.NotNull(name, nameof(name)); |
|||
|
|||
var template = GetOrNull(name); |
|||
|
|||
if (template == null) |
|||
var permission = await GetOrNullAsync(name); |
|||
if (permission == null) |
|||
{ |
|||
throw new AbpException("Undefined template: " + name); |
|||
throw new AbpException("Undefined Template: " + name); |
|||
} |
|||
|
|||
return template; |
|||
return permission; |
|||
} |
|||
|
|||
public virtual IReadOnlyList<TemplateDefinition> GetAll() |
|||
public virtual async Task<TemplateDefinition> GetOrNullAsync(string name) |
|||
{ |
|||
return TemplateDefinitions.Value.Values.ToImmutableList(); |
|||
} |
|||
Check.NotNull(name, nameof(name)); |
|||
|
|||
public virtual TemplateDefinition GetOrNull(string name) |
|||
{ |
|||
return TemplateDefinitions.Value.GetOrDefault(name); |
|||
return await StaticStore.GetOrNullAsync(name) ?? await DynamicStore.GetOrNullAsync(name); |
|||
} |
|||
|
|||
protected virtual IDictionary<string, TemplateDefinition> CreateTextTemplateDefinitions() |
|||
public virtual async Task<IReadOnlyList<TemplateDefinition>> GetAllAsync() |
|||
{ |
|||
var templates = new Dictionary<string, TemplateDefinition>(); |
|||
|
|||
using (var scope = ServiceProvider.CreateScope()) |
|||
{ |
|||
var providers = Options |
|||
.DefinitionProviders |
|||
.Select(p => scope.ServiceProvider.GetRequiredService(p) as ITemplateDefinitionProvider) |
|||
.ToList(); |
|||
|
|||
var context = new TemplateDefinitionContext(templates); |
|||
var staticTemplates = await StaticStore.GetAllAsync(); |
|||
var staticTemplateNames = staticTemplates |
|||
.Select(p => p.Name) |
|||
.ToImmutableHashSet(); |
|||
|
|||
foreach (var provider in providers) |
|||
{ |
|||
provider.PreDefine(context); |
|||
} |
|||
|
|||
foreach (var provider in providers) |
|||
{ |
|||
provider.Define(context); |
|||
} |
|||
|
|||
foreach (var provider in providers) |
|||
{ |
|||
provider.PostDefine(context); |
|||
} |
|||
} |
|||
var dynamicTemplates = await DynamicStore.GetAllAsync(); |
|||
|
|||
return templates; |
|||
/* We prefer static Templates over dynamics */ |
|||
return staticTemplates.Concat(dynamicTemplates.Where(d => !staticTemplateNames.Contains(d.Name))).ToImmutableList(); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,8 @@ |
|||
namespace Volo.Abp.TextTemplating.VirtualFiles; |
|||
|
|||
public class TemplateContentFileInfo |
|||
{ |
|||
public string FileName { get; set; } |
|||
|
|||
public string FileContent { get; set; } |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace Volo.Abp.TextTemplating.VirtualFiles; |
|||
|
|||
public class TemplateContentFileProvider : ITransientDependency |
|||
{ |
|||
protected IVirtualFileProvider VirtualFileProvider { get; } |
|||
|
|||
public TemplateContentFileProvider(IVirtualFileProvider virtualFileProvider) |
|||
{ |
|||
VirtualFileProvider = virtualFileProvider; |
|||
} |
|||
|
|||
public async Task<List<TemplateContentFileInfo>> GetFilesAsync(TemplateDefinition templateDefinition) |
|||
{ |
|||
var files = new List<TemplateContentFileInfo>(); |
|||
|
|||
var virtualPath = templateDefinition.GetVirtualFilePathOrNull(); |
|||
if (virtualPath == null) |
|||
{ |
|||
return files; |
|||
} |
|||
|
|||
var fileInfo = VirtualFileProvider.GetFileInfo(virtualPath); |
|||
if (!fileInfo.Exists) |
|||
{ |
|||
var directoryContents = VirtualFileProvider.GetDirectoryContents(virtualPath); |
|||
if (!directoryContents.Exists) |
|||
{ |
|||
throw new AbpException("Could not find a file/folder at the location: " + virtualPath); |
|||
} |
|||
|
|||
fileInfo = new VirtualDirectoryFileInfo(virtualPath, virtualPath, DateTimeOffset.UtcNow); |
|||
} |
|||
|
|||
if (fileInfo.IsDirectory) |
|||
{ |
|||
//TODO: Configure file extensions.
|
|||
var folderReader = new VirtualFolderLocalizedTemplateContentReader(new[] { ".tpl", ".cshtml" }); |
|||
await folderReader.ReadContentsAsync(VirtualFileProvider, virtualPath); |
|||
files.AddRange(folderReader.GetFiles()); |
|||
} |
|||
else |
|||
{ |
|||
var singleFileReader = new FileInfoLocalizedTemplateContentReader(); |
|||
await singleFileReader.ReadContentsAsync(fileInfo); |
|||
files.Add(singleFileReader.GetFile()); |
|||
} |
|||
|
|||
return files; |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using System.Threading.Tasks; |
|||
using Shouldly; |
|||
using Volo.Abp.TextTemplating.VirtualFiles; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.TextTemplating.Razor; |
|||
|
|||
public class RazorTemplateContentFileProvider_Tests : TemplateContentFileProvider_Tests<RazorTextTemplatingTestModule> |
|||
{ |
|||
[Fact] |
|||
public async Task GetRazorFilesAsync() |
|||
{ |
|||
var definition = await TemplateDefinitionManager.GetAsync(TestTemplates.WelcomeEmail); |
|||
var files = await TemplateContentFileProvider.GetFilesAsync(definition); |
|||
files.Count.ShouldBe(2); |
|||
files.ShouldContain(x => x.FileName == "en.cshtml" && x.FileContent.Contains("Welcome @Model.Name to the abp.io!")); |
|||
files.ShouldContain(x => x.FileName == "tr.cshtml" && x.FileContent.Contains("Merhaba @Model.Name, abp.io'ya hoşgeldiniz!")); |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using System.Threading.Tasks; |
|||
using Shouldly; |
|||
using Volo.Abp.TextTemplating.VirtualFiles; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.TextTemplating.Scriban.SampleTemplates; |
|||
|
|||
public class ScribanTemplateContentFileProvider_Tests : TemplateContentFileProvider_Tests<ScribanTextTemplatingTestModule> |
|||
{ |
|||
[Fact] |
|||
public async Task GetScribanFilesAsync() |
|||
{ |
|||
var definition = await TemplateDefinitionManager.GetAsync(TestTemplates.WelcomeEmail); |
|||
var files = await TemplateContentFileProvider.GetFilesAsync(definition); |
|||
files.Count.ShouldBe(2); |
|||
files.ShouldContain(x => x.FileName == "en.tpl" && x.FileContent.Contains("Welcome {{model.name}} to the abp.io!")); |
|||
files.ShouldContain(x => x.FileName == "tr.tpl" && x.FileContent.Contains("Merhaba {{model.name}}, abp.io'ya hoşgeldiniz!")); |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using System.Threading.Tasks; |
|||
using Shouldly; |
|||
using Volo.Abp.Modularity; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.TextTemplating.VirtualFiles; |
|||
|
|||
public abstract class TemplateContentFileProvider_Tests<TStartupModule> : AbpTextTemplatingTestBase<TStartupModule> |
|||
where TStartupModule : IAbpModule |
|||
{ |
|||
protected readonly TemplateContentFileProvider TemplateContentFileProvider; |
|||
protected readonly ITemplateDefinitionManager TemplateDefinitionManager; |
|||
|
|||
protected TemplateContentFileProvider_Tests() |
|||
{ |
|||
TemplateContentFileProvider = GetRequiredService<TemplateContentFileProvider>(); |
|||
TemplateDefinitionManager = GetRequiredService<ITemplateDefinitionManager>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetFilesAsync() |
|||
{ |
|||
var definition = await TemplateDefinitionManager.GetAsync(TestTemplates.HybridTemplateScriban); |
|||
|
|||
var files = await TemplateContentFileProvider.GetFilesAsync(definition); |
|||
files.Count.ShouldBe(1); |
|||
files.ShouldContain(x => x.FileName == "TestScribanTemplate.tpl" && x.FileContent == "Hello {{model.name}}, {{L \"HowAreYou\" }}"); |
|||
} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Shouldly; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.OpenIddict.Applications; |
|||
|
|||
public class AbpApplicationManager_Tests : OpenIddictDomainTestBase |
|||
{ |
|||
private readonly IAbpApplicationManager _applicationManager; |
|||
private readonly AbpOpenIddictTestData _testData; |
|||
|
|||
public AbpApplicationManager_Tests() |
|||
{ |
|||
_applicationManager = ServiceProvider.GetRequiredService<IAbpApplicationManager>(); |
|||
_testData = ServiceProvider.GetRequiredService<AbpOpenIddictTestData>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Populate_Descriptor_With_Application_Test() |
|||
{ |
|||
var app1 = (await _applicationManager.FindByClientIdAsync(_testData.App1ClientId)).As<OpenIddictApplicationModel>(); |
|||
|
|||
var descriptor = new AbpApplicationDescriptor(); |
|||
await _applicationManager.PopulateAsync(descriptor, app1); |
|||
|
|||
app1.ClientUri.ShouldNotBeNull(); |
|||
app1.LogoUri.ShouldNotBeNull(); |
|||
|
|||
descriptor.ClientUri.ShouldBe(app1.ClientUri); |
|||
descriptor.LogoUri.ShouldBe(app1.LogoUri); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Populate_Application_With_Descriptor_Test() |
|||
{ |
|||
var app1 = (await _applicationManager.FindByClientIdAsync(_testData.App1ClientId)).As<OpenIddictApplicationModel>(); |
|||
|
|||
var descriptor = new AbpApplicationDescriptor() |
|||
{ |
|||
ClientUri = "https://new.com", |
|||
LogoUri = "https://new.com/logo.png" |
|||
}; |
|||
await _applicationManager.PopulateAsync(app1, descriptor); |
|||
|
|||
app1.ClientUri.ShouldBe(descriptor.ClientUri); |
|||
app1.LogoUri.ShouldBe(descriptor.LogoUri); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue