mirror of https://github.com/Squidex/squidex.git
Browse Source
# Conflicts: # backend/extensions/Squidex.Extensions/Actions/Comment/CommentActionHandler.cs # backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Extensions/EventFluidExtensions.cs # backend/src/Squidex.Domain.Apps.Core.Operations/IUrlGenerator.cs # backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository.cs # backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs # backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollectionAll.cs # backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryIdsAsync.cs # backend/src/Squidex.Domain.Apps.Entities/Apps/AppDomainObject.cs # backend/src/Squidex.Domain.Apps.Entities/Apps/Plans/UsageGate.cs # backend/src/Squidex.Domain.Apps.Entities/Assets/AssetDomainObject.cs # backend/src/Squidex.Domain.Apps.Entities/Assets/AssetFolderDomainObject.cs # backend/src/Squidex.Domain.Apps.Entities/Comments/CommentsCommandMiddleware.cs # backend/src/Squidex.Domain.Apps.Entities/Contents/ContentDomainObject.cs # backend/src/Squidex.Domain.Apps.Entities/Contents/Repositories/IContentRepository.cs # backend/src/Squidex.Domain.Apps.Entities/Rules/IRuleEnqueuer.cs # backend/src/Squidex.Domain.Apps.Entities/Rules/RuleDomainObject.cs # backend/src/Squidex.Domain.Apps.Entities/Rules/RuleEnqueuer.cs # backend/src/Squidex.Web/Services/UrlGenerator.cs # backend/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs # backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs # backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/TestData/FakeUrlGenerator.cs # backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDomainObjectTests.cspull/590/head
184 changed files with 2603 additions and 943 deletions
@ -0,0 +1,42 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.ComponentModel.DataAnnotations; |
|||
using Squidex.Domain.Apps.Core.HandleRules; |
|||
using Squidex.Domain.Apps.Core.Rules; |
|||
using Squidex.Infrastructure.Validation; |
|||
|
|||
namespace Squidex.Extensions.Actions.CreateContent |
|||
{ |
|||
[RuleAction( |
|||
Title = "CreateContent", |
|||
IconImage = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 28 28'><path d='M21.875 28H6.125A6.087 6.087 0 010 21.875V6.125A6.087 6.087 0 016.125 0h15.75A6.087 6.087 0 0128 6.125v15.75A6.088 6.088 0 0121.875 28zM6.125 1.75A4.333 4.333 0 001.75 6.125v15.75a4.333 4.333 0 004.375 4.375h15.75a4.333 4.333 0 004.375-4.375V6.125a4.333 4.333 0 00-4.375-4.375H6.125z'/><path d='M13.125 12.25H7.35c-1.575 0-2.888-1.313-2.888-2.888V7.349c0-1.575 1.313-2.888 2.888-2.888h5.775c1.575 0 2.887 1.313 2.887 2.888v2.013c0 1.575-1.312 2.888-2.887 2.888zM7.35 6.212c-.613 0-1.138.525-1.138 1.138v2.012A1.16 1.16 0 007.35 10.5h5.775a1.16 1.16 0 001.138-1.138V7.349a1.16 1.16 0 00-1.138-1.138H7.35zM22.662 16.713H5.337c-.525 0-.875-.35-.875-.875s.35-.875.875-.875h17.237c.525 0 .875.35.875.875s-.35.875-.787.875zM15.138 21.262h-9.8c-.525 0-.875-.35-.875-.875s.35-.875.875-.875h9.713c.525 0 .875.35.875.875s-.35.875-.787.875z'/></svg>", |
|||
IconColor = "#3389ff", |
|||
Display = "Create content", |
|||
Description = "Create a a new content item for any schema.")] |
|||
public sealed class CreateContentAction : RuleAction |
|||
{ |
|||
[LocalizedRequired] |
|||
[Display(Name = "Data", Description = "The content data.")] |
|||
[DataType(DataType.MultilineText)] |
|||
[Formattable] |
|||
public string Data { get; set; } |
|||
|
|||
[LocalizedRequired] |
|||
[Display(Name = "Schema", Description = "The name of the schema.")] |
|||
[DataType(DataType.Text)] |
|||
public string Schema { get; set; } |
|||
|
|||
[Display(Name = "Client", Description = "An optional client name.")] |
|||
[DataType(DataType.Text)] |
|||
public string Client { get; set; } |
|||
|
|||
[Display(Name = "Publish", Description = "Publish the content.")] |
|||
[DataType(DataType.Text)] |
|||
public bool Publish { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,87 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Core.HandleRules; |
|||
using Squidex.Domain.Apps.Core.Rules.EnrichedEvents; |
|||
using Squidex.Domain.Apps.Entities; |
|||
using Squidex.Domain.Apps.Entities.Schemas; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Infrastructure.Json; |
|||
using Command = Squidex.Domain.Apps.Entities.Contents.Commands.CreateContent; |
|||
|
|||
namespace Squidex.Extensions.Actions.CreateContent |
|||
{ |
|||
public sealed class CreateContentActionHandler : RuleActionHandler<CreateContentAction, Command> |
|||
{ |
|||
private const string Description = "Create a content"; |
|||
private readonly ICommandBus commandBus; |
|||
private readonly IAppProvider appProvider; |
|||
private readonly IJsonSerializer jsonSerializer; |
|||
|
|||
public CreateContentActionHandler(RuleEventFormatter formatter, IAppProvider appProvider, ICommandBus commandBus, IJsonSerializer jsonSerializer) |
|||
: base(formatter) |
|||
{ |
|||
Guard.NotNull(appProvider, nameof(appProvider)); |
|||
Guard.NotNull(commandBus, nameof(commandBus)); |
|||
Guard.NotNull(jsonSerializer, nameof(jsonSerializer)); |
|||
|
|||
this.appProvider = appProvider; |
|||
this.commandBus = commandBus; |
|||
this.jsonSerializer = jsonSerializer; |
|||
} |
|||
|
|||
protected override async Task<(string Description, Command Data)> CreateJobAsync(EnrichedEvent @event, CreateContentAction action) |
|||
{ |
|||
var ruleJob = new Command |
|||
{ |
|||
AppId = @event.AppId, |
|||
}; |
|||
|
|||
var schema = await appProvider.GetSchemaAsync(@event.AppId.Id, action.Schema, true); |
|||
|
|||
if (schema == null) |
|||
{ |
|||
throw new InvalidOperationException($"Cannot find schema '{action.Schema}'"); |
|||
} |
|||
|
|||
ruleJob.SchemaId = schema.NamedId(); |
|||
|
|||
var json = await FormatAsync(action.Data, @event); |
|||
|
|||
ruleJob.Data = jsonSerializer.Deserialize<NamedContentData>(json); |
|||
|
|||
if (!string.IsNullOrEmpty(action.Client)) |
|||
{ |
|||
ruleJob.Actor = new RefToken(RefTokenType.Client, action.Client); |
|||
} |
|||
else if (@event is EnrichedUserEventBase userEvent) |
|||
{ |
|||
ruleJob.Actor = userEvent.Actor; |
|||
} |
|||
|
|||
ruleJob.Publish = action.Publish; |
|||
|
|||
return (Description, ruleJob); |
|||
} |
|||
|
|||
protected override async Task<Result> ExecuteJobAsync(Command job, CancellationToken ct = default) |
|||
{ |
|||
var command = job; |
|||
|
|||
command.FromRule = true; |
|||
|
|||
await commandBus.PublishAsync(command); |
|||
|
|||
return Result.Success($"Created to: {job.SchemaId.Name}"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Microsoft.Extensions.Configuration; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Squidex.Infrastructure.Plugins; |
|||
|
|||
namespace Squidex.Extensions.Actions.CreateContent |
|||
{ |
|||
public sealed class CreateContentPlugin : IPlugin |
|||
{ |
|||
public void ConfigureServices(IServiceCollection services, IConfiguration config) |
|||
{ |
|||
services.AddRuleAction<CreateContentAction, CreateContentActionHandler>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using MongoDB.Bson.Serialization; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.MongoDb.Assets |
|||
{ |
|||
internal static class Fields |
|||
{ |
|||
private static readonly Lazy<string> AssetIdField = new Lazy<string>(GetAssetIdField); |
|||
private static readonly Lazy<string> AssetFolderIdField = new Lazy<string>(GetAssetFolderIdField); |
|||
|
|||
public static string AssetId => AssetIdField.Value; |
|||
|
|||
public static string AssetFolderId => AssetFolderIdField.Value; |
|||
|
|||
private static string GetAssetIdField() |
|||
{ |
|||
return BsonClassMap.LookupClassMap(typeof(MongoAssetEntity)).GetMemberMap(nameof(MongoAssetEntity.Id)).ElementName; |
|||
} |
|||
|
|||
private static string GetAssetFolderIdField() |
|||
{ |
|||
return BsonClassMap.LookupClassMap(typeof(MongoAssetFolderEntity)).GetMemberMap(nameof(MongoAssetFolderEntity.Id)).ElementName; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using MongoDB.Bson.Serialization; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents |
|||
{ |
|||
internal static class Fields |
|||
{ |
|||
private static readonly Lazy<string> IdField = new Lazy<string>(GetIdField); |
|||
private static readonly Lazy<string> SchemaIdField = new Lazy<string>(GetSchemaIdField); |
|||
|
|||
public static string Id => IdField.Value; |
|||
|
|||
public static string SchemaId => SchemaIdField.Value; |
|||
|
|||
private static string GetIdField() |
|||
{ |
|||
return BsonClassMap.LookupClassMap(typeof(MongoContentEntity)).GetMemberMap(nameof(MongoContentEntity.Id)).ElementName; |
|||
} |
|||
|
|||
private static string GetSchemaIdField() |
|||
{ |
|||
return BsonClassMap.LookupClassMap(typeof(MongoContentEntity)).GetMemberMap(nameof(MongoContentEntity.IndexedSchemaId)).ElementName; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using MongoDB.Driver; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.MongoDb; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations |
|||
{ |
|||
internal sealed class QueryReferrersAsync : OperationBase |
|||
{ |
|||
protected override Task PrepareAsync(CancellationToken ct = default) |
|||
{ |
|||
var index = |
|||
new CreateIndexModel<MongoContentEntity>(Index |
|||
.Ascending(x => x.ReferencedIds) |
|||
.Ascending(x => x.IndexedAppId) |
|||
.Ascending(x => x.IsDeleted)); |
|||
|
|||
return Collection.Indexes.CreateOneAsync(index, cancellationToken: ct); |
|||
} |
|||
|
|||
public async Task<bool> DoAsync(DomainId appId, DomainId contentId) |
|||
{ |
|||
var currentId = DomainId.Combine(appId, contentId); |
|||
|
|||
var filter = |
|||
Filter.And( |
|||
Filter.AnyEq(x => x.ReferencedIds, appId), |
|||
Filter.Eq(x => x.IndexedAppId, appId), |
|||
Filter.Ne(x => x.IsDeleted, true), |
|||
Filter.Ne(x => x.Id, currentId)); |
|||
|
|||
var hasReferrerAsync = |
|||
await Collection.Find(filter).Only(x => x.Id) |
|||
.AnyAsync(); |
|||
|
|||
return hasReferrerAsync; |
|||
} |
|||
} |
|||
} |
|||
@ -1,28 +0,0 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
|
|||
<head> |
|||
<meta charset="utf-8"> |
|||
|
|||
<!-- Load the editor sdk from the local folder or https://cloud.squidex.io/scripts/editor-sdk.js --> |
|||
<script src="editor-sdk.js"></script> |
|||
</head> |
|||
|
|||
<body> |
|||
<textarea style="width: 100%; box-sizing: border-box; height: 100px;" name="content" id="editor"></textarea> |
|||
|
|||
<script> |
|||
var element = document.getElementById('editor'); |
|||
|
|||
// When the field is instantiated it notified the UI that it has been loaded. |
|||
var field = new SquidexFormField(); |
|||
|
|||
field.onInit(function (context) { |
|||
if (context) { |
|||
element.innerHTML = JSON.stringify(context, null, 2); |
|||
} |
|||
}); |
|||
</script> |
|||
</body> |
|||
|
|||
</html> |
|||
@ -0,0 +1,47 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
|
|||
<head> |
|||
<meta charset="utf-8"> |
|||
|
|||
<!-- Load the editor sdk from the local folder or https://cloud.squidex.io/scripts/editor-sdk.js --> |
|||
<script src="editor-sdk.js"></script> |
|||
|
|||
<style> |
|||
textarea { |
|||
box-sizing: border-box; |
|||
resize: none; |
|||
overflow: hidden; |
|||
width: 100%; |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
<body> |
|||
<script> |
|||
function grow(element) { |
|||
element.style.height = "5px"; |
|||
element.style.height = (element.scrollHeight)+"px"; |
|||
} |
|||
</script> |
|||
|
|||
<textarea oninput="grow(this)" name="content" id="editor"></textarea> |
|||
|
|||
<script> |
|||
var element = document.getElementById('editor'); |
|||
|
|||
// When the field is instantiated it notifies the UI that it has been loaded. |
|||
// |
|||
// Furthermore it sends the current size to the parent. |
|||
var field = new SquidexFormField(); |
|||
|
|||
// Init is called once with a context that contains the app name, schema name and authentication information. |
|||
field.onInit(function (context) { |
|||
element.innerHTML = JSON.stringify(context, null, 2); |
|||
|
|||
grow(element); |
|||
}); |
|||
</script> |
|||
</body> |
|||
|
|||
</html> |
|||
@ -0,0 +1,49 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
|
|||
<head> |
|||
<meta charset="utf-8"> |
|||
|
|||
<!-- Load the editor sdk from the local folder or https://cloud.squidex.io/scripts/editor-sdk.js --> |
|||
<script src="editor-sdk.js"></script> |
|||
|
|||
<style> |
|||
textarea { |
|||
box-sizing: border-box; |
|||
border: 0; |
|||
border-radius: 0; |
|||
resize: none; |
|||
overflow: hidden; |
|||
width: 100%; |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
<body> |
|||
<script> |
|||
function grow(element) { |
|||
element.style.height = "5px"; |
|||
element.style.height = (element.scrollHeight)+"px"; |
|||
} |
|||
</script> |
|||
|
|||
<textarea oninput="grow(this)" name="content" id="editor"></textarea> |
|||
|
|||
<script> |
|||
var element = document.getElementById('editor'); |
|||
|
|||
// When the plugin is instantiated it notifies the UI that it has been loaded. |
|||
// |
|||
// Furthermore it sends the current size to the parent. |
|||
var plugin = new SquidexPlugin(); |
|||
|
|||
// The content is only available when it is used as a sidebar plugin for single content items. |
|||
plugin.onContentChanged(function (content) { |
|||
element.innerHTML = JSON.stringify(content, null, 2); |
|||
|
|||
grow(element); |
|||
}); |
|||
</script> |
|||
</body> |
|||
|
|||
</html> |
|||
@ -0,0 +1,49 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
|
|||
<head> |
|||
<meta charset="utf-8"> |
|||
|
|||
<!-- Load the editor sdk from the local folder or https://cloud.squidex.io/scripts/editor-sdk.js --> |
|||
<script src="editor-sdk.js"></script> |
|||
|
|||
<style> |
|||
textarea { |
|||
box-sizing: border-box; |
|||
border: 0; |
|||
border-radius: 0; |
|||
resize: none; |
|||
overflow: hidden; |
|||
width: 100%; |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
<body> |
|||
<script> |
|||
function grow(element) { |
|||
element.style.height = "5px"; |
|||
element.style.height = (element.scrollHeight)+"px"; |
|||
} |
|||
</script> |
|||
|
|||
<textarea oninput="grow(this)" name="content" id="editor"></textarea> |
|||
|
|||
<script> |
|||
var element = document.getElementById('editor'); |
|||
|
|||
// When the field is instantiated it notifies the UI that it has been loaded. |
|||
// |
|||
// Furthermore it sends the current size to the parent. |
|||
var plugin = new SquidexPlugin(); |
|||
|
|||
// Init is called once with a context that contains the app name, schema name and authentication information. |
|||
plugin.onInit(function (context) { |
|||
element.innerHTML = JSON.stringify(context, null, 2); |
|||
|
|||
grow(element); |
|||
}); |
|||
</script> |
|||
</body> |
|||
|
|||
</html> |
|||
@ -0,0 +1,118 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
|
|||
<head> |
|||
<meta charset="utf-8"> |
|||
|
|||
<!-- Load the editor sdk from the local folder or https://cloud.squidex.io/scripts/editor-sdk.js --> |
|||
<script src="editor-sdk.js"></script> |
|||
|
|||
<script src="https://cdn.jsdelivr.net/npm/algoliasearch@4.0.0/dist/algoliasearch-lite.umd.js" integrity="sha256-MfeKq2Aw9VAkaE9Caes2NOxQf6vUa8Av0JqcUXUGkd0=" crossorigin="anonymous"></script> |
|||
<script src="https://cdn.jsdelivr.net/npm/instantsearch.js@4.0.0/dist/instantsearch.production.min.js" integrity="sha256-6S7q0JJs/Kx4kb/fv0oMjS855QTz5Rc2hh9AkIUjUsk=" crossorigin="anonymous"></script> |
|||
|
|||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/instantsearch.css@7.3.1/themes/algolia-min.css" integrity="sha256-HB49n/BZjuqiCtQQf49OdZn63XuKFaxcIHWf0HNKte8=" crossorigin="anonymous"> |
|||
|
|||
<style> |
|||
.container { |
|||
min-height: 400px; |
|||
} |
|||
|
|||
.ais-Hits { |
|||
margin-top: 1rem; |
|||
} |
|||
|
|||
.ais-Hits-item { |
|||
margin-right: 0; |
|||
margin-top: 5px; |
|||
width: 100%; |
|||
} |
|||
|
|||
.button-click { |
|||
margin-top: 5px; |
|||
color: #3389ff; |
|||
cursor: pointer; |
|||
display: inline-block; |
|||
} |
|||
|
|||
.button-click:hover { |
|||
text-decoration: underline; |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
<body> |
|||
<script> |
|||
function grow(element) { |
|||
element.style.height = "5px"; |
|||
element.style.height = (element.scrollHeight)+"px"; |
|||
} |
|||
</script> |
|||
|
|||
<div class="container"> |
|||
<div id="searchbox"></div> |
|||
|
|||
<div id="hits"></div> |
|||
</div> |
|||
|
|||
<script> |
|||
var element = document.getElementById('editor'); |
|||
|
|||
// When the field is instantiated it notifies the UI that it has been loaded. |
|||
// |
|||
// Furthermore it sends the current size to the parent. |
|||
var plugin = new SquidexPlugin(); |
|||
|
|||
// Init is called once with a context that contains the app name, schema name and authentication information. |
|||
plugin.onInit(function (context) { |
|||
var searchClient = algoliasearch('CFNTEE51PJ', 'afa3a7605277b85348c6fa160fb5cecc'); |
|||
|
|||
var search = instantsearch({ indexName: 'test', searchClient }); |
|||
|
|||
document.addEventListener('click', event => { |
|||
if (event.target.matches('.button-click')) { |
|||
var id = event.target.getAttribute('data-object-id'); |
|||
|
|||
// We cannot directly navigate to in the iframe because it would only change the URL of the iframe. |
|||
plugin.navigate(`/app/${context.appName}/content/${context.schemaName}`); |
|||
// plugin.navigate(`/app/${context.appName}/content/${context.schemaName}/${id}`); |
|||
} |
|||
}); |
|||
|
|||
search.addWidgets([ |
|||
instantsearch.widgets.searchBox({ |
|||
container: '#searchbox', |
|||
}), |
|||
|
|||
instantsearch.widgets.hits({ |
|||
container: '#hits', |
|||
templates: { |
|||
item(hit, bindEvent) { |
|||
return ` |
|||
<div> |
|||
<div class="hit-name"> |
|||
${instantsearch.highlight({ |
|||
attribute: 'firstname', |
|||
hit, |
|||
})} |
|||
${instantsearch.highlight({ |
|||
attribute: 'lastname', |
|||
hit, |
|||
})} |
|||
|
|||
<div> |
|||
<a data-object-id="${hit.objectID}" class="button-click">EDIT</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
`; |
|||
} |
|||
}, |
|||
}) |
|||
]); |
|||
|
|||
search.start(); |
|||
}); |
|||
</script> |
|||
</body> |
|||
|
|||
</html> |
|||
@ -0,0 +1,60 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Orleans; |
|||
using Orleans.TestingHost; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Infrastructure.Orleans |
|||
{ |
|||
[Trait("Category", "Dependencies")] |
|||
public class AsyncLocalTests |
|||
{ |
|||
public interface IAsyncLocalGrain : IGrainWithStringKey |
|||
{ |
|||
public Task<int> GetValueAsync(); |
|||
} |
|||
|
|||
public class AsyncLocalGrain : Grain, IAsyncLocalGrain |
|||
{ |
|||
private readonly AsyncLocal<int> temp = new AsyncLocal<int>(); |
|||
|
|||
public Task<int> GetValueAsync() |
|||
{ |
|||
temp.Value++; |
|||
|
|||
return Task.FromResult(temp.Value); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_use_async_local() |
|||
{ |
|||
var cluster = |
|||
new TestClusterBuilder(1) |
|||
.Build(); |
|||
|
|||
await cluster.DeployAsync(); |
|||
|
|||
var grain = cluster.GrainFactory.GetGrain<IAsyncLocalGrain>(SingleGrain.Id); |
|||
|
|||
var result1 = await grain.GetValueAsync(); |
|||
var result2 = await grain.GetValueAsync(); |
|||
|
|||
await cluster.KillSiloAsync(cluster.Silos[0]); |
|||
await cluster.StartAdditionalSiloAsync(); |
|||
|
|||
var result3 = await grain.GetValueAsync(); |
|||
|
|||
Assert.Equal(1, result1); |
|||
Assert.Equal(1, result2); |
|||
Assert.Equal(1, result3); |
|||
} |
|||
} |
|||
} |
|||
@ -1 +1,9 @@ |
|||
<sqx-comments [commentsId]="commentsId | async"></sqx-comments> |
|||
<sqx-panel desiredWidth="20rem" isBlank="true" [isLazyLoaded]="false" grid="true"> |
|||
<ng-container title> |
|||
{{ 'comments.title' | sqxTranslate }} |
|||
</ng-container> |
|||
|
|||
<ng-container content> |
|||
<sqx-comments [commentsId]="commentsId | async"></sqx-comments> |
|||
</ng-container> |
|||
</sqx-panel> |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue