Browse Source

Medium task improved.

pull/306/head
Sebastian 8 years ago
parent
commit
3d5c5b6135
  1. 4
      src/Squidex.Domain.Apps.Core.Model/Rules/Actions/MediumAction.cs
  2. 11
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/FastlyActionHandler.cs
  3. 58
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/MediumActionHandler.cs
  4. 8
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/SlackActionHandler.cs
  5. 8
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/WebhookActionHandler.cs
  6. 6
      src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs
  7. 18
      src/Squidex.Domain.Apps.Entities/Assets/AssetGrain.cs
  8. 3
      src/Squidex.Domain.Apps.Entities/Assets/IAssetGrain.cs
  9. 14
      src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs
  10. 35
      src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs
  11. 3
      src/Squidex.Domain.Apps.Entities/Contents/IContentGrain.cs
  12. 7
      src/Squidex.Domain.Apps.Entities/Rules/EventEnricher.cs
  13. 5
      src/Squidex.Domain.Apps.Entities/Rules/Guards/RuleActionValidator.cs
  14. 6
      src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs
  15. 34
      src/Squidex.Domain.Apps.Entities/SquidexDomainObjectGrainLogSnapshots.cs
  16. 6
      src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs
  17. 5
      src/Squidex.Infrastructure/Commands/DomainObjectGrainBase.cs
  18. 6
      src/Squidex.Infrastructure/Commands/LogSnapshotDomainObjectGrain.cs
  19. 12
      src/Squidex.Infrastructure/Http/DumpFormatter.cs
  20. 10
      src/Squidex/Areas/Api/Controllers/Rules/Models/Actions/MediumActionDto.cs
  21. 28
      src/Squidex/app/features/rules/pages/rules/actions/medium-action.component.html
  22. 6
      src/Squidex/app/features/rules/pages/rules/actions/medium-action.component.ts
  23. 75
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentVersionLoaderTests.cs
  24. 22
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Actions/MediumActionTests.cs
  25. 16
      tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs
  26. 6
      tests/Squidex.Infrastructure.Tests/Commands/LogSnapshotDomainObjectGrainTests.cs

4
src/Squidex.Domain.Apps.Core.Model/Rules/Actions/MediumAction.cs

@ -14,10 +14,6 @@ namespace Squidex.Domain.Apps.Core.Rules.Actions
{
public string AccessToken { get; set; }
public string Author { get; set; }
public string Publication { get; set; }
public string Tags { get; set; }
public string Title { get; set; }

11
src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/FastlyActionHandler.cs

@ -10,6 +10,7 @@ using System.Net.Http;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
using Squidex.Domain.Apps.Core.Rules.Actions;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Http;
#pragma warning disable SA1649 // File name must match first type name
@ -47,22 +48,24 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
return (null, new InvalidOperationException("The action cannot handle this event."));
}
var requestMsg = BuildRequest(job);
var request = BuildRequest(job);
HttpResponseMessage response = null;
try
{
response = await HttpClientPool.GetHttpClient().SendAsync(requestMsg);
var valueWatch = ValueStopwatch.StartNew();
response = await HttpClientPool.GetHttpClient().SendAsync(request);
var responseString = await response.Content.ReadAsStringAsync();
var requestDump = DumpFormatter.BuildDump(requestMsg, response, null, responseString, TimeSpan.Zero, false);
var requestDump = DumpFormatter.BuildDump(request, response, null, responseString, TimeSpan.Zero, false);
return (requestDump, null);
}
catch (Exception ex)
{
var requestDump = DumpFormatter.BuildDump(requestMsg, response, null, ex.ToString(), TimeSpan.Zero, false);
var requestDump = DumpFormatter.BuildDump(request, response, null, ex.ToString(), TimeSpan.Zero, false);
return (requestDump, ex);
}

58
src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/MediumActionHandler.cs

@ -22,8 +22,6 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
{
public sealed class MediumJob
{
public string RequestUrl { get; set; }
public string RequestBody { get; set; }
public string AccessToken { get; set; }
@ -44,11 +42,6 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
protected override (string Description, MediumJob Data) CreateJob(EnrichedEvent @event, MediumAction action)
{
var requestUrl =
!string.IsNullOrWhiteSpace(action.Author) ?
$"https://api.medium.com/v1/users/{action.Author}/posts" :
$"https://api.medium.com/v1/publication/{action.Publication}/posts";
var requestBody =
new JObject(
new JProperty("title", formatter.Format(action.Title, @event)),
@ -57,12 +50,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
new JProperty("canonicalUrl", formatter.Format(action.CanonicalUrl, @event)),
new JProperty("tags", ParseTags(@event, action)));
var ruleJob = new MediumJob
{
AccessToken = action.AccessToken,
RequestUrl = requestUrl,
RequestBody = requestBody.ToString(Formatting.Indented)
};
var ruleJob = new MediumJob { AccessToken = action.AccessToken, RequestBody = requestBody.ToString(Formatting.Indented) };
return (Description, ruleJob);
}
@ -91,17 +79,36 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(MediumJob job)
{
var requestBody = job.RequestBody;
var requestMessage = BuildRequest(job, requestBody);
string id;
HttpResponseMessage response = null;
var meRequest = BuildMeRequest(job);
try
{
response = await HttpClientPool.GetHttpClient().SendAsync(meRequest);
var responseString = await response.Content.ReadAsStringAsync();
var responseJson = JToken.Parse(responseString);
id = responseJson["data"]["id"].ToString();
}
catch (Exception ex)
{
var requestDump = DumpFormatter.BuildDump(meRequest, response, ex.ToString());
return (requestDump, ex);
}
var postRequestBody = job.RequestBody;
var postRequest = BuildPostRequest(job, postRequestBody, id);
try
{
response = await HttpClientPool.GetHttpClient().SendAsync(requestMessage);
response = await HttpClientPool.GetHttpClient().SendAsync(postRequest);
var responseString = await response.Content.ReadAsStringAsync();
var requestDump = DumpFormatter.BuildDump(requestMessage, response, requestBody, responseString, TimeSpan.Zero, false);
var requestDump = DumpFormatter.BuildDump(postRequest, response, postRequestBody, responseString);
Exception ex = null;
@ -114,15 +121,15 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
}
catch (Exception ex)
{
var requestDump = DumpFormatter.BuildDump(requestMessage, response, requestBody, ex.ToString(), TimeSpan.Zero, false);
var requestDump = DumpFormatter.BuildDump(postRequest, response, postRequestBody, ex.ToString());
return (requestDump, ex);
}
}
private static HttpRequestMessage BuildRequest(MediumJob job, string requestBody)
private static HttpRequestMessage BuildPostRequest(MediumJob job, string requestBody, string id)
{
var request = new HttpRequestMessage(HttpMethod.Post, job.RequestUrl)
var request = new HttpRequestMessage(HttpMethod.Post, $"https://api.medium.com/v1/users/{id}/posts")
{
Content = new StringContent(requestBody, Encoding.UTF8, "application/json")
};
@ -133,5 +140,16 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
return request;
}
private static HttpRequestMessage BuildMeRequest(MediumJob job)
{
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.medium.com/v1/me");
request.Headers.Add("Accept", "application/json");
request.Headers.Add("Accept-Charset", "utf-8");
request.Headers.Add("Authorization", $"Bearer {job.AccessToken}");
return request;
}
}
}

8
src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/SlackActionHandler.cs

@ -67,22 +67,22 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(SlackJob job)
{
var requestBody = job.Body;
var requestMessage = BuildRequest(job, requestBody);
var request = BuildRequest(job, requestBody);
HttpResponseMessage response = null;
try
{
response = await HttpClientPool.GetHttpClient().SendAsync(requestMessage);
response = await HttpClientPool.GetHttpClient().SendAsync(request);
var responseString = await response.Content.ReadAsStringAsync();
var requestDump = DumpFormatter.BuildDump(requestMessage, response, requestBody, responseString, TimeSpan.Zero, false);
var requestDump = DumpFormatter.BuildDump(request, response, requestBody, responseString);
return (requestDump, null);
}
catch (Exception ex)
{
var requestDump = DumpFormatter.BuildDump(requestMessage, response, requestBody, ex.ToString(), TimeSpan.Zero, false);
var requestDump = DumpFormatter.BuildDump(request, response, requestBody, ex.ToString());
return (requestDump, ex);
}

8
src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/WebhookActionHandler.cs

@ -69,16 +69,16 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(WebhookJob job)
{
var requestBody = job.Body;
var requestMessage = BuildRequest(job, requestBody);
var request = BuildRequest(job, requestBody);
HttpResponseMessage response = null;
try
{
response = await HttpClientPool.GetHttpClient().SendAsync(requestMessage);
response = await HttpClientPool.GetHttpClient().SendAsync(request);
var responseString = await response.Content.ReadAsStringAsync();
var requestDump = DumpFormatter.BuildDump(requestMessage, response, requestBody, responseString, TimeSpan.Zero, false);
var requestDump = DumpFormatter.BuildDump(request, response, requestBody, responseString);
Exception ex = null;
@ -91,7 +91,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
}
catch (Exception ex)
{
var requestDump = DumpFormatter.BuildDump(requestMessage, response, requestBody, ex.ToString(), TimeSpan.Zero, false);
var requestDump = DumpFormatter.BuildDump(request, response, requestBody, ex.ToString());
return (requestDump, ex);
}

6
src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs

@ -72,14 +72,14 @@ namespace Squidex.Domain.Apps.Entities.Apps
});
case AssignContributor assigneContributor:
return UpdateReturnAsync(assigneContributor, async c =>
return UpdateReturnAsync(assigneContributor, (Func<AssignContributor, Task<object>>)(async c =>
{
await GuardAppContributors.CanAssign(Snapshot.Contributors, c, userResolver, appPlansProvider.GetPlan(Snapshot.Plan?.PlanId));
AssignContributor(c);
return EntityCreatedResult.Create(c.ContributorId, NewVersion);
});
return EntityCreatedResult.Create(c.ContributorId, (long)base.Version);
}));
case RemoveContributor removeContributor:
return UpdateAsync(removeContributor, c =>

18
src/Squidex.Domain.Apps.Entities/Assets/AssetGrain.cs

@ -22,7 +22,7 @@ using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities.Assets
{
public sealed class AssetGrain : SquidexDomainObjectGrain<AssetState>, IAssetGrain
public sealed class AssetGrain : SquidexDomainObjectGrainLogSnapshots<AssetState>, IAssetGrain
{
public AssetGrain(IStore<Guid> store, ISemanticLog log)
: base(store, log)
@ -34,23 +34,23 @@ namespace Squidex.Domain.Apps.Entities.Assets
switch (command)
{
case CreateAsset createRule:
return CreateReturnAsync(createRule, c =>
return CreateReturnAsync(createRule, (Func<CreateAsset, object>)(c =>
{
GuardAsset.CanCreate(c);
Create(c);
return new AssetSavedResult(NewVersion, Snapshot.FileVersion);
});
return new AssetSavedResult((long)base.Version, Snapshot.FileVersion);
}));
case UpdateAsset updateRule:
return UpdateReturnAsync(updateRule, c =>
return UpdateReturnAsync(updateRule, (Func<UpdateAsset, object>)(c =>
{
GuardAsset.CanUpdate(c);
Update(c);
return new AssetSavedResult(NewVersion, Snapshot.FileVersion);
});
return new AssetSavedResult((long)base.Version, Snapshot.FileVersion);
}));
case RenameAsset renameAsset:
return UpdateAsync(renameAsset, c =>
{
@ -140,9 +140,9 @@ namespace Squidex.Domain.Apps.Entities.Assets
return Snapshot.Apply(@event);
}
public Task<J<IAssetEntity>> GetStateAsync()
public Task<J<IAssetEntity>> GetStateAsync(long version = EtagVersion.Any)
{
return J.AsTask<IAssetEntity>(Snapshot);
return J.AsTask<IAssetEntity>(GetSnapshot(version));
}
}
}

3
src/Squidex.Domain.Apps.Entities/Assets/IAssetGrain.cs

@ -6,6 +6,7 @@
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Orleans;
@ -13,6 +14,6 @@ namespace Squidex.Domain.Apps.Entities.Assets
{
public interface IAssetGrain : IDomainObjectGrain
{
Task<J<IAssetEntity>> GetStateAsync();
Task<J<IAssetEntity>> GetStateAsync(long version = EtagVersion.Any);
}
}

14
src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs

@ -26,7 +26,7 @@ using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities.Contents
{
public sealed class ContentGrain : SquidexDomainObjectGrain<ContentState>, IContentGrain
public sealed class ContentGrain : SquidexDomainObjectGrainLogSnapshots<ContentState>, IContentGrain
{
private readonly IAppProvider appProvider;
private readonly IAssetRepository assetRepository;
@ -60,7 +60,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
switch (command)
{
case CreateContent createContent:
return CreateReturnAsync(createContent, async c =>
return CreateReturnAsync(createContent, (Func<CreateContent, Task<object>>)(async c =>
{
var ctx = await CreateContext(c.AppId.Id, c.SchemaId.Id, () => "Failed to create content.");
@ -77,8 +77,8 @@ namespace Squidex.Domain.Apps.Entities.Contents
Create(c);
return EntityCreatedResult.Create(c.Data, NewVersion);
});
return EntityCreatedResult.Create(c.Data, (long)base.Version);
}));
case UpdateContent updateContent:
return UpdateReturnAsync(updateContent, c =>
@ -216,7 +216,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
}
}
return new ContentDataChangedResult(newData, NewVersion);
return new ContentDataChangedResult(newData, Version);
}
public void Create(CreateContent command)
@ -307,9 +307,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
return operationContext;
}
public Task<J<IContentEntity>> GetStateAsync()
public Task<J<IContentEntity>> GetStateAsync(long version = EtagVersion.Any)
{
return J.AsTask<IContentEntity>(Snapshot);
return J.AsTask<IContentEntity>(GetSnapshot(version));
}
}
}

35
src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs

@ -7,53 +7,38 @@
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Contents.State;
using Orleans;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities.Contents
{
public sealed class ContentVersionLoader : IContentVersionLoader
{
private readonly IStore<Guid> store;
private readonly FieldRegistry registry;
private readonly IGrainFactory grainFactory;
public ContentVersionLoader(IStore<Guid> store, FieldRegistry registry)
public ContentVersionLoader(IGrainFactory grainFactory)
{
Guard.NotNull(store, nameof(store));
Guard.NotNull(registry, nameof(registry));
Guard.NotNull(grainFactory, nameof(grainFactory));
this.store = store;
this.registry = registry;
this.grainFactory = grainFactory;
}
public async Task<IContentEntity> LoadAsync(Guid id, long version)
{
using (Profiler.TraceMethod<ContentVersionLoader>())
{
var content = new ContentState();
var persistence = store.WithEventSourcing<ContentGrain, Guid>(id, e =>
{
if (content.Version < version)
{
content = content.Apply(e);
content.Version++;
}
});
var grain = grainFactory.GetGrain<IContentGrain>(id);
await persistence.ReadAsync();
var content = await grain.GetStateAsync(version);
if (content.Version != version)
if (content.Value == null || content.Value.Version != version)
{
throw new DomainObjectNotFoundException(id.ToString(), typeof(IContentEntity));
}
return content;
return content.Value;
}
}
}
}
}

3
src/Squidex.Domain.Apps.Entities/Contents/IContentGrain.cs

@ -6,6 +6,7 @@
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Orleans;
@ -13,6 +14,6 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
public interface IContentGrain : IDomainObjectGrain
{
Task<J<IContentEntity>> GetStateAsync();
Task<J<IContentEntity>> GetStateAsync(long version = EtagVersion.Any);
}
}

7
src/Squidex.Domain.Apps.Entities/Rules/EventEnricher.cs

@ -76,10 +76,9 @@ namespace Squidex.Domain.Apps.Entities.Rules
var asset =
(await grainFactory
.GetGrain<IAssetGrain>(assetEvent.AssetId)
.GetStateAsync()).Value;
.GetStateAsync(@event.Headers.EventStreamNumber())).Value;
SimpleMapper.Map(asset, result);
SimpleMapper.Map(assetEvent, result);
switch (assetEvent)
{
@ -105,14 +104,12 @@ namespace Squidex.Domain.Apps.Entities.Rules
var content =
(await grainFactory
.GetGrain<IContentGrain>(contentEvent.ContentId)
.GetStateAsync()).Value;
.GetStateAsync(@event.Headers.EventStreamNumber())).Value;
SimpleMapper.Map(content, result);
result.Data = content.Data ?? content.DataDraft;
SimpleMapper.Map(contentEvent, result);
switch (contentEvent)
{
case ContentCreated _:

5
src/Squidex.Domain.Apps.Entities/Rules/Guards/RuleActionValidator.cs

@ -116,11 +116,6 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards
errors.Add(new ValidationError("Access token is required.", nameof(action.AccessToken)));
}
if (string.IsNullOrWhiteSpace(action.Author) && string.IsNullOrWhiteSpace(action.Publication))
{
errors.Add(new ValidationError("Author or publication is required.", nameof(action.Author), nameof(action.Publication)));
}
if (string.IsNullOrWhiteSpace(action.Content))
{
errors.Add(new ValidationError("Content is required.", nameof(action.Content)));

6
src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs

@ -47,7 +47,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
switch (command)
{
case AddField addField:
return UpdateReturnAsync(addField, c =>
return UpdateReturnAsync(addField, (Func<AddField, object>)(c =>
{
GuardSchemaField.CanAdd(Snapshot.SchemaDef, c);
@ -64,8 +64,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas
id = ((IArrayField)Snapshot.SchemaDef.FieldsById[c.ParentFieldId.Value]).FieldsByName[c.Name].Id;
}
return EntityCreatedResult.Create(id, NewVersion);
});
return EntityCreatedResult.Create(id, (long)base.Version);
}));
case CreateSchema createSchema:
return CreateAsync(createSchema, async c =>

34
src/Squidex.Domain.Apps.Entities/SquidexDomainObjectGrainLogSnapshots.cs

@ -0,0 +1,34 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities
{
public abstract class SquidexDomainObjectGrainLogSnapshots<T> : LogSnapshotDomainObjectGrain<T> where T : IDomainState, new()
{
protected SquidexDomainObjectGrainLogSnapshots(IStore<Guid> store, ISemanticLog log)
: base(store, log)
{
}
public override void RaiseEvent(Envelope<IEvent> @event)
{
if (@event.Payload is AppEvent appEvent)
{
@event.SetAppId(appEvent.AppId.Id);
}
base.RaiseEvent(@event);
}
}
}

6
src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs

@ -34,8 +34,12 @@ namespace Squidex.Infrastructure.Commands
protected sealed override void ApplyEvent(Envelope<IEvent> @event)
{
var newVersion = Version + 1;
var snapshotNew = OnEvent(@event);
snapshot = OnEvent(@event);
snapshot.Version = NewVersion + 1;
snapshot.Version = newVersion;
}
protected sealed override void RestorePreviousSnapshot(T previousSnapshot, long previousVersion)

5
src/Squidex.Infrastructure/Commands/DomainObjectGrainBase.cs

@ -31,11 +31,6 @@ namespace Squidex.Infrastructure.Commands
get { return Snapshot.Version; }
}
public long NewVersion
{
get { return Snapshot.Version + uncomittedEvents.Count; }
}
public abstract T Snapshot { get; }
protected DomainObjectGrainBase(ISemanticLog log)

6
src/Squidex.Infrastructure/Commands/LogSnapshotDomainObjectGrain.cs

@ -15,7 +15,7 @@ using Squidex.Infrastructure.States;
namespace Squidex.Infrastructure.Commands
{
public abstract class MultiSnapshotDomainObjectGrain<T> : DomainObjectGrainBase<T> where T : IDomainState, new()
public abstract class LogSnapshotDomainObjectGrain<T> : DomainObjectGrainBase<T> where T : IDomainState, new()
{
private readonly IStore<Guid> store;
private readonly List<T> snapshots = new List<T> { new T { Version = EtagVersion.Empty } };
@ -26,7 +26,7 @@ namespace Squidex.Infrastructure.Commands
get { return snapshots.Last(); }
}
protected MultiSnapshotDomainObjectGrain(IStore<Guid> store, ISemanticLog log)
protected LogSnapshotDomainObjectGrain(IStore<Guid> store, ISemanticLog log)
: base(log)
{
Guard.NotNull(log, nameof(log));
@ -58,7 +58,7 @@ namespace Squidex.Infrastructure.Commands
{
var snapshot = OnEvent(@event);
snapshot.Version = NewVersion + 1;
snapshot.Version = Version + 1;
snapshots.Add(snapshot);
}

12
src/Squidex.Infrastructure/Http/DumpFormatter.cs

@ -15,7 +15,17 @@ namespace Squidex.Infrastructure.Http
{
public static class DumpFormatter
{
public static string BuildDump(HttpRequestMessage request, HttpResponseMessage response, string requestBody, string responseBody, TimeSpan elapsed, bool isTimeout)
public static string BuildDump(HttpRequestMessage request, HttpResponseMessage response, string responseBody)
{
return BuildDump(request, response, null, responseBody, TimeSpan.Zero, false);
}
public static string BuildDump(HttpRequestMessage request, HttpResponseMessage response, string requestBody, string responseBody)
{
return BuildDump(request, response, requestBody, responseBody, TimeSpan.Zero, false);
}
public static string BuildDump(HttpRequestMessage request, HttpResponseMessage response, string requestBody, string responseBody, TimeSpan elapsed, bool isTimeout = false)
{
var writer = new StringBuilder();

10
src/Squidex/Areas/Api/Controllers/Rules/Models/Actions/MediumactionDto.cs → src/Squidex/Areas/Api/Controllers/Rules/Models/Actions/MediumActionDto.cs

@ -22,16 +22,6 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models.Actions
[Required]
public string AccessToken { get; set; }
/// <summary>
/// The author name.
/// </summary>
public string Author { get; set; }
/// <summary>
/// The author name.
/// </summary>
public string Publication { get; set; }
/// <summary>
/// The optional comma separated list of tags.
/// </summary>

28
src/Squidex/app/features/rules/pages/rules/actions/medium-action.component.html

@ -15,34 +15,6 @@
</div>
</div>
<div class="form-group row">
<label class="col col-3 col-form-label" for="author">Author</label>
<div class="col col-9">
<sqx-control-errors for="author" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="author" formControlName="author" />
<small class="form-text text-muted">
The name of the author. You can also define the publication.
</small>
</div>
</div>
<div class="form-group row">
<label class="col col-3 col-form-label" for="publication">Publication</label>
<div class="col col-9">
<sqx-control-errors for="publication" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="publication" formControlName="publication" />
<small class="form-text text-muted">
The name of the publication. You can also define the author.
</small>
</div>
</div>
<div class="form-group row">
<label class="col col-3 col-form-label" for="title">Title</label>

6
src/Squidex/app/features/rules/pages/rules/actions/medium-action.component.ts

@ -39,12 +39,6 @@ export class MediumActionComponent implements OnInit {
Validators.required
]));
this.actionForm.setControl('author',
new FormControl(this.action.author || ''));
this.actionForm.setControl('publication',
new FormControl(this.action.publication || ''));
this.actionForm.setControl('canonicalUrl',
new FormControl(this.action.canonicalUrl || ''));

75
tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentVersionLoaderTests.cs

@ -0,0 +1,75 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using FakeItEasy;
using Orleans;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Orleans;
using Squidex.Infrastructure.States;
using Xunit;
namespace Squidex.Domain.Apps.Entities.Contents
{
public class ContentVersionLoaderTests
{
private readonly IGrainFactory grainFactory = A.Fake<IGrainFactory>();
private readonly IContentGrain grain = A.Fake<IContentGrain>();
private readonly FieldRegistry fieldRegistry = new FieldRegistry(new TypeNameRegistry());
private readonly Guid id = Guid.NewGuid();
private readonly ContentVersionLoader sut;
public ContentVersionLoaderTests()
{
A.CallTo(() => grainFactory.GetGrain<IContentGrain>(id, null))
.Returns(grain);
sut = new ContentVersionLoader(grainFactory);
}
[Fact]
public async Task Should_throw_exception_if_no_state_returned()
{
A.CallTo(() => grain.GetStateAsync(10))
.Returns(new J<IContentEntity>(null));
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.LoadAsync(id, 10));
}
[Fact]
public async Task Should_throw_exception_if_state_has_other_version()
{
var entity = A.Fake<IContentEntity>();
A.CallTo(() => entity.Version)
.Returns(5);
A.CallTo(() => grain.GetStateAsync(10))
.Returns(J.Of(entity));
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.LoadAsync(id, 10));
}
[Fact]
public async Task Should_return_content_from_state()
{
var entity = A.Fake<IContentEntity>();
A.CallTo(() => entity.Version)
.Returns(10);
A.CallTo(() => grain.GetStateAsync(10))
.Returns(J.Of(entity));
var result = await sut.LoadAsync(id, 10);
Assert.Same(entity, result);
}
}
}

22
tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Actions/MediumActionTests.cs

@ -19,7 +19,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards.Actions
[Fact]
public async Task Should_add_error_if_access_token_is_null()
{
var action = new MediumAction { AccessToken = null, Author = "author", Title = "title", Content = "content" };
var action = new MediumAction { AccessToken = null, Title = "title", Content = "content" };
var errors = await RuleActionValidator.ValidateAsync(action);
@ -30,24 +30,10 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards.Actions
});
}
[Fact]
public async Task Should_add_error_if_author_is_null()
{
var action = new MediumAction { AccessToken = "token", Author = null, Title = "title", Content = "content" };
var errors = await RuleActionValidator.ValidateAsync(action);
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Author or publication is required.", "Author", "Publication")
});
}
[Fact]
public async Task Should_add_error_if_title_null()
{
var action = new MediumAction { AccessToken = "token", Author = "author", Title = null, Content = "content" };
var action = new MediumAction { AccessToken = "token", Title = null, Content = "content" };
var errors = await RuleActionValidator.ValidateAsync(action);
@ -61,7 +47,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards.Actions
[Fact]
public async Task Should_add_error_if_content_is_null()
{
var action = new MediumAction { AccessToken = "token", Author = "author", Title = "title", Content = null };
var action = new MediumAction { AccessToken = "token", Title = "title", Content = null };
var errors = await RuleActionValidator.ValidateAsync(action);
@ -75,7 +61,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards.Actions
[Fact]
public async Task Should_not_add_error_if_values_are_valid()
{
var action = new MediumAction { AccessToken = "token", Author = "author", Title = "title", Content = "content" };
var action = new MediumAction { AccessToken = "token", Title = "title", Content = "content" };
var errors = await RuleActionValidator.ValidateAsync(action);

16
tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs

@ -24,7 +24,8 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers
public abstract class HandlerTestBase<T, TState> where T : IDomainObjectGrain
{
private readonly IStore<Guid> store = A.Fake<IStore<Guid>>();
private readonly IPersistence<TState> persistence = A.Fake<IPersistence<TState>>();
private readonly IPersistence<TState> persistence1 = A.Fake<IPersistence<TState>>();
private readonly IPersistence persistence2 = A.Fake<IPersistence>();
protected RefToken User { get; } = new RefToken("subject", Guid.NewGuid().ToString());
@ -58,9 +59,18 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers
protected HandlerTestBase()
{
A.CallTo(() => store.WithSnapshotsAndEventSourcing(A<Type>.Ignored, Id, A<Func<TState, Task>>.Ignored, A<Func<Envelope<IEvent>, Task>>.Ignored))
.Returns(persistence);
.Returns(persistence1);
A.CallTo(() => persistence.WriteEventsAsync(A<IEnumerable<Envelope<IEvent>>>.Ignored))
A.CallTo(() => store.WithEventSourcing(A<Type>.Ignored, Id, A<Func<Envelope<IEvent>, Task>>.Ignored))
.Returns(persistence2);
A.CallTo(() => persistence1.WriteEventsAsync(A<IEnumerable<Envelope<IEvent>>>.Ignored))
.Invokes(new Action<IEnumerable<Envelope<IEvent>>>(events =>
{
LastEvents = events;
}));
A.CallTo(() => persistence2.WriteEventsAsync(A<IEnumerable<Envelope<IEvent>>>.Ignored))
.Invokes(new Action<IEnumerable<Envelope<IEvent>>>(events =>
{
LastEvents = events;

6
tests/Squidex.Infrastructure.Tests/Commands/LogSnapshotDomainObjectGrainTests.cs

@ -20,7 +20,7 @@ using Xunit;
namespace Squidex.Infrastructure.Commands
{
public class MultiSnapshotDomainObjectGrainTests
public class LogSnapshotDomainObjectGrainTests
{
private readonly IStore<Guid> store = A.Fake<IStore<Guid>>();
private readonly ISnapshotStore<MyDomainState, Guid> snapshotStore = A.Fake<ISnapshotStore<MyDomainState, Guid>>();
@ -53,7 +53,7 @@ namespace Squidex.Infrastructure.Commands
public int Value { get; set; }
}
public sealed class MyDomainObject : MultiSnapshotDomainObjectGrain<MyDomainState>
public sealed class MyDomainObject : LogSnapshotDomainObjectGrain<MyDomainState>
{
public MyDomainObject(IStore<Guid> store)
: base(store, A.Dummy<ISemanticLog>())
@ -102,7 +102,7 @@ namespace Squidex.Infrastructure.Commands
}
}
public MultiSnapshotDomainObjectGrainTests()
public LogSnapshotDomainObjectGrainTests()
{
A.CallTo(() => store.WithEventSourcing(typeof(MyDomainObject), id, A<Func<Envelope<IEvent>, Task>>.Ignored))
.Returns(persistence);

Loading…
Cancel
Save