Browse Source

Merge branch 'master' of github.com:Squidex/squidex

pull/514/head
Sebastian 6 years ago
parent
commit
3654e2973c
  1. 2
      backend/src/Squidex.Domain.Apps.Core.Model/Rules/RuleJob.cs
  2. 10
      backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs
  3. 1
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/DefaultValidatorsFactory.cs
  4. 1
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/IValidatorsFactory.cs
  5. 57
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleEventRepository.cs
  6. 2
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj
  7. 12
      backend/src/Squidex.Domain.Apps.Entities/Rules/Repositories/IRuleEventRepository.cs
  8. 12
      backend/src/Squidex.Domain.Apps.Entities/Rules/RuleDequeuerGrain.cs
  9. 28
      backend/src/Squidex.Domain.Apps.Entities/Rules/RuleJobUpdate.cs
  10. 47
      backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/GrainRuleRunnerService.cs
  11. 22
      backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/IRuleRunnerGrain.cs
  12. 21
      backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/IRuleRunnerService.cs
  13. 202
      backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/RuleRunnerGrain.cs
  14. 2
      backend/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj
  15. 1
      backend/src/Squidex.Domain.Users/UserManagerExtensions.cs
  16. 1
      backend/src/Squidex.Domain.Users/UserValues.cs
  17. 4
      backend/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoExtensions.cs
  18. 4
      backend/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj
  19. 35
      backend/src/Squidex.Infrastructure/UniqueConstraintException.cs
  20. 13
      backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleDto.cs
  21. 8
      backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs
  22. 21
      backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RulesDto.cs
  23. 61
      backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs
  24. 4
      backend/src/Squidex/Config/Domain/RuleServices.cs
  25. 2
      backend/src/Squidex/Squidex.csproj
  26. 1
      backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesTests.cs
  27. 2
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs
  28. 3
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs
  29. 1
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/AssetsValidatorTests.cs
  30. 1
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/ReferencesValidatorTests.cs
  31. 11
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDequeuerTests.cs
  32. 10
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs
  33. 2
      backend/tools/Migrate_00/Migrate_00.csproj
  34. 16
      frontend/app/app.module.ts
  35. 20
      frontend/app/app.routes.ts
  36. 4
      frontend/app/app.ts
  37. 1
      frontend/app/features/administration/administration-area.component.ts
  38. 11
      frontend/app/features/administration/declarations.ts
  39. 4
      frontend/app/features/administration/guards/unset-user.guard.spec.ts
  40. 3
      frontend/app/features/administration/guards/unset-user.guard.ts
  41. 4
      frontend/app/features/administration/guards/user-must-exist.guard.spec.ts
  42. 6
      frontend/app/features/administration/guards/user-must-exist.guard.ts
  43. 1
      frontend/app/features/administration/internal.ts
  44. 26
      frontend/app/features/administration/module.ts
  45. 1
      frontend/app/features/administration/pages/cluster/cluster-page.component.ts
  46. 1
      frontend/app/features/administration/pages/event-consumers/event-consumer.component.ts
  47. 6
      frontend/app/features/administration/pages/event-consumers/event-consumers-page.component.ts
  48. 9
      frontend/app/features/administration/pages/restore/restore-page.component.ts
  49. 9
      frontend/app/features/administration/pages/users/user-page.component.ts
  50. 1
      frontend/app/features/administration/pages/users/user.component.ts
  51. 1
      frontend/app/features/administration/pages/users/users-page.component.ts
  52. 8
      frontend/app/features/administration/services/event-consumers.service.spec.ts
  53. 9
      frontend/app/features/administration/services/event-consumers.service.ts
  54. 8
      frontend/app/features/administration/services/users.service.spec.ts
  55. 10
      frontend/app/features/administration/services/users.service.ts
  56. 9
      frontend/app/features/administration/state/event-consumers.state.spec.ts
  57. 8
      frontend/app/features/administration/state/event-consumers.state.ts
  58. 2
      frontend/app/features/administration/state/users.forms.ts
  59. 18
      frontend/app/features/administration/state/users.state.spec.ts
  60. 20
      frontend/app/features/administration/state/users.state.ts
  61. 1
      frontend/app/features/api/api-area.component.ts
  62. 3
      frontend/app/features/api/declarations.ts
  63. 2
      frontend/app/features/api/index.ts
  64. 12
      frontend/app/features/api/module.ts
  65. 11
      frontend/app/features/api/pages/graphql/graphql-page.component.ts
  66. 2
      frontend/app/features/apps/declarations.ts
  67. 2
      frontend/app/features/apps/index.ts
  68. 8
      frontend/app/features/apps/module.ts
  69. 14
      frontend/app/features/apps/pages/apps-page.component.ts
  70. 1
      frontend/app/features/apps/pages/news-dialog.component.ts
  71. 1
      frontend/app/features/apps/pages/onboarding-dialog.component.ts
  72. 2
      frontend/app/features/assets/declarations.ts
  73. 2
      frontend/app/features/assets/index.ts
  74. 8
      frontend/app/features/assets/module.ts
  75. 1
      frontend/app/features/assets/pages/asset-tags.component.ts
  76. 8
      frontend/app/features/assets/pages/assets-filters-page.component.ts
  77. 11
      frontend/app/features/assets/pages/assets-page.component.ts
  78. 10
      frontend/app/features/content/declarations.ts
  79. 2
      frontend/app/features/content/index.ts
  80. 48
      frontend/app/features/content/module.ts
  81. 1
      frontend/app/features/content/pages/comments/comments-page.component.ts
  82. 1
      frontend/app/features/content/pages/content/content-event.component.ts
  83. 15
      frontend/app/features/content/pages/content/content-field.component.ts
  84. 16
      frontend/app/features/content/pages/content/content-history-page.component.ts
  85. 25
      frontend/app/features/content/pages/content/content-page.component.ts
  86. 1
      frontend/app/features/content/pages/content/field-languages.component.ts
  87. 10
      frontend/app/features/content/pages/contents/contents-filters-page.component.ts
  88. 21
      frontend/app/features/content/pages/contents/contents-page.component.ts
  89. 1
      frontend/app/features/content/pages/schemas/schemas-page.component.ts
  90. 1
      frontend/app/features/content/shared/content-status.component.ts
  91. 3
      frontend/app/features/content/shared/due-time-selector.component.ts
  92. 9
      frontend/app/features/content/shared/forms/array-editor.component.ts
  93. 12
      frontend/app/features/content/shared/forms/array-item.component.ts
  94. 13
      frontend/app/features/content/shared/forms/assets-editor.component.ts
  95. 10
      frontend/app/features/content/shared/forms/field-editor.component.ts
  96. 10
      frontend/app/features/content/shared/forms/stock-photo-editor.component.ts
  97. 8
      frontend/app/features/content/shared/list/content-list-cell.directive.ts
  98. 11
      frontend/app/features/content/shared/list/content-list-field.component.ts
  99. 10
      frontend/app/features/content/shared/list/content-list-header.component.ts
  100. 1
      frontend/app/features/content/shared/list/content-value-editor.component.ts

2
backend/src/Squidex.Domain.Apps.Core.Model/Rules/RuleJob.cs

@ -12,7 +12,7 @@ namespace Squidex.Domain.Apps.Core.Rules
{ {
public sealed class RuleJob public sealed class RuleJob
{ {
public Guid EventId { get; set; } public Guid Id { get; set; }
public Guid AppId { get; set; } public Guid AppId { get; set; }

10
backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs

@ -67,7 +67,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules
this.log = log; this.log = log;
} }
public virtual async Task<List<RuleJob>> CreateJobsAsync(Rule rule, Guid ruleId, Envelope<IEvent> @event) public virtual async Task<List<RuleJob>> CreateJobsAsync(Rule rule, Guid ruleId, Envelope<IEvent> @event, bool ignoreStale = false)
{ {
Guard.NotNull(rule); Guard.NotNull(rule);
Guard.NotNull(@event); Guard.NotNull(@event);
@ -107,13 +107,13 @@ namespace Squidex.Domain.Apps.Core.HandleRules
@event.Headers.Timestamp() : @event.Headers.Timestamp() :
now; now;
var expires = eventTime.Plus(Constants.ExpirationTime); if (!ignoreStale && eventTime.Plus(Constants.StaleTime) < now)
if (eventTime.Plus(Constants.StaleTime) < now)
{ {
return result; return result;
} }
var expires = eventTime.Plus(Constants.ExpirationTime);
if (!triggerHandler.Trigger(typed.Payload, rule.Trigger, ruleId)) if (!triggerHandler.Trigger(typed.Payload, rule.Trigger, ruleId))
{ {
return result; return result;
@ -141,12 +141,12 @@ namespace Squidex.Domain.Apps.Core.HandleRules
var job = new RuleJob var job = new RuleJob
{ {
Id = Guid.NewGuid(),
ActionData = json, ActionData = json,
ActionName = actionName, ActionName = actionName,
AppId = enrichedEvent.AppId.Id, AppId = enrichedEvent.AppId.Id,
Created = now, Created = now,
Description = actionData.Description, Description = actionData.Description,
EventId = @event.Headers.EventId(),
EventName = enrichedEvent.Name, EventName = enrichedEvent.Name,
ExecutionPartition = enrichedEvent.Partition, ExecutionPartition = enrichedEvent.Partition,
Expires = expires, Expires = expires,

1
backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/DefaultValidatorsFactory.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.ValidateContent.Validators; using Squidex.Domain.Apps.Core.ValidateContent.Validators;

1
backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/IValidatorsFactory.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;

57
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleEventRepository.cs

@ -59,7 +59,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
return Collection.Find(x => x.NextAttempt < now).ForEachAsync(callback, ct); return Collection.Find(x => x.NextAttempt < now).ForEachAsync(callback, ct);
} }
public async Task<IReadOnlyList<IRuleEventEntity>> QueryByAppAsync(Guid appId, Guid? ruleId = null, int skip = 0, int take = 20) public async Task<IResultList<IRuleEventEntity>> QueryByAppAsync(Guid appId, Guid? ruleId = null, int skip = 0, int take = 20)
{ {
var filter = Filter.Eq(x => x.AppId, appId); var filter = Filter.Eq(x => x.AppId, appId);
@ -68,11 +68,12 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
filter = Filter.And(filter, Filter.Eq(x => x.RuleId, ruleId)); filter = Filter.And(filter, Filter.Eq(x => x.RuleId, ruleId));
} }
var ruleEventEntities = var taskForItems = Collection.Find(filter).Skip(skip).Limit(take).SortByDescending(x => x.Created).ToListAsync();
await Collection.Find(filter).Skip(skip).Limit(take).SortByDescending(x => x.Created) var taskForCount = Collection.Find(filter).CountDocumentsAsync();
.ToListAsync();
return ruleEventEntities; await Task.WhenAll(taskForItems, taskForCount);
return ResultList.Create(taskForCount.Result, taskForItems.Result);
} }
public async Task<IRuleEventEntity> FindAsync(Guid id) public async Task<IRuleEventEntity> FindAsync(Guid id)
@ -84,37 +85,16 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
return ruleEvent; return ruleEvent;
} }
public async Task<int> CountByAppAsync(Guid appId)
{
return (int)await Collection.CountDocumentsAsync(x => x.AppId == appId);
}
public Task EnqueueAsync(Guid id, Instant nextAttempt) public Task EnqueueAsync(Guid id, Instant nextAttempt)
{ {
return Collection.UpdateOneAsync(x => x.Id == id, Update.Set(x => x.NextAttempt, nextAttempt)); return Collection.UpdateOneAsync(x => x.Id == id, Update.Set(x => x.NextAttempt, nextAttempt));
} }
public async Task EnqueueAsync(RuleJob job, Instant nextAttempt) public async Task EnqueueAsync(RuleJob job, Instant nextAttempt, CancellationToken ct = default)
{ {
var entity = SimpleMapper.Map(job, new MongoRuleEventEntity { Job = job, Created = nextAttempt, NextAttempt = nextAttempt }); var entity = SimpleMapper.Map(job, new MongoRuleEventEntity { Job = job, Created = nextAttempt, NextAttempt = nextAttempt });
if (job.EventId != default) await Collection.InsertOneIfNotExistsAsync(entity, ct);
{
entity.Id = job.EventId;
}
else
{
entity.Id = Guid.NewGuid();
}
try
{
await Collection.InsertOneIfNotExistsAsync(entity);
}
catch (MongoWriteException ex) when (ex.WriteError.Category == ServerErrorCategory.DuplicateKey)
{
throw new UniqueConstraintException();
}
} }
public Task CancelAsync(Guid id) public Task CancelAsync(Guid id)
@ -125,23 +105,26 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
.Set(x => x.JobResult, RuleJobResult.Cancelled)); .Set(x => x.JobResult, RuleJobResult.Cancelled));
} }
public async Task MarkSentAsync(RuleJob job, string? dump, RuleResult result, RuleJobResult jobResult, TimeSpan elapsed, Instant finished, Instant? nextCall) public async Task UpdateAsync(RuleJob job, RuleJobUpdate update)
{ {
if (result == RuleResult.Success) Guard.NotNull(job);
Guard.NotNull(update);
if (update.ExecutionResult == RuleResult.Success)
{ {
await statisticsCollection.IncrementSuccess(job.AppId, job.RuleId, finished); await statisticsCollection.IncrementSuccess(job.AppId, job.RuleId, update.Finished);
} }
else else
{ {
await statisticsCollection.IncrementFailed(job.AppId, job.RuleId, finished); await statisticsCollection.IncrementFailed(job.AppId, job.RuleId, update.Finished);
} }
await Collection.UpdateOneAsync(x => x.Id == job.EventId, await Collection.UpdateOneAsync(x => x.Id == job.Id,
Update Update
.Set(x => x.Result, result) .Set(x => x.Result, update.ExecutionResult)
.Set(x => x.LastDump, dump) .Set(x => x.LastDump, update.ExecutionDump)
.Set(x => x.JobResult, jobResult) .Set(x => x.JobResult, update.JobResult)
.Set(x => x.NextAttempt, nextCall) .Set(x => x.NextAttempt, update.JobNext)
.Inc(x => x.NumCalls, 1)); .Inc(x => x.NumCalls, 1));
} }

2
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj

@ -17,7 +17,7 @@
<ProjectReference Include="..\Squidex.Domain.Apps.Entities\Squidex.Domain.Apps.Entities.csproj" /> <ProjectReference Include="..\Squidex.Domain.Apps.Entities\Squidex.Domain.Apps.Entities.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MongoDB.Driver" Version="2.10.2" /> <PackageReference Include="MongoDB.Driver" Version="2.10.3" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" /> <PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" /> <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" /> <PackageReference Include="System.ValueTuple" Version="4.5.0" />

12
backend/src/Squidex.Domain.Apps.Entities/Rules/Repositories/IRuleEventRepository.cs

@ -10,28 +10,26 @@ using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using NodaTime; using NodaTime;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Core.Rules;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Rules.Repositories namespace Squidex.Domain.Apps.Entities.Rules.Repositories
{ {
public interface IRuleEventRepository public interface IRuleEventRepository
{ {
Task EnqueueAsync(RuleJob job, Instant nextAttempt); Task UpdateAsync(RuleJob job, RuleJobUpdate update);
Task EnqueueAsync(RuleJob job, Instant nextAttempt, CancellationToken ct = default);
Task EnqueueAsync(Guid id, Instant nextAttempt); Task EnqueueAsync(Guid id, Instant nextAttempt);
Task CancelAsync(Guid id); Task CancelAsync(Guid id);
Task MarkSentAsync(RuleJob job, string? dump, RuleResult result, RuleJobResult jobResult, TimeSpan elapsed, Instant finished, Instant? nextCall);
Task QueryPendingAsync(Instant now, Func<IRuleEventEntity, Task> callback, CancellationToken ct = default); Task QueryPendingAsync(Instant now, Func<IRuleEventEntity, Task> callback, CancellationToken ct = default);
Task<int> CountByAppAsync(Guid appId);
Task<IReadOnlyList<RuleStatistics>> QueryStatisticsByAppAsync(Guid appId); Task<IReadOnlyList<RuleStatistics>> QueryStatisticsByAppAsync(Guid appId);
Task<IReadOnlyList<IRuleEventEntity>> QueryByAppAsync(Guid appId, Guid? ruleId = null, int skip = 0, int take = 20); Task<IResultList<IRuleEventEntity>> QueryByAppAsync(Guid appId, Guid? ruleId = null, int skip = 0, int take = 20);
Task<IRuleEventEntity> FindAsync(Guid id); Task<IRuleEventEntity> FindAsync(Guid id);
} }

12
backend/src/Squidex.Domain.Apps.Entities/Rules/RuleDequeuerGrain.cs

@ -105,7 +105,17 @@ namespace Squidex.Domain.Apps.Entities.Rules
var now = clock.GetCurrentInstant(); var now = clock.GetCurrentInstant();
await ruleEventRepository.MarkSentAsync(@event.Job, response.Dump, response.Status, jobResult, elapsed, now, jobDelay); var update = new RuleJobUpdate
{
Elapsed = elapsed,
ExecutionDump = response.Dump,
ExecutionResult = response.Status,
Finished = now,
JobNext = jobDelay,
JobResult = ComputeJobResult(response.Status, jobDelay),
};
await ruleEventRepository.UpdateAsync(@event.Job, update);
} }
catch (Exception ex) catch (Exception ex)
{ {

28
backend/src/Squidex.Domain.Apps.Entities/Rules/RuleJobUpdate.cs

@ -0,0 +1,28 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using NodaTime;
using Squidex.Domain.Apps.Core.HandleRules;
namespace Squidex.Domain.Apps.Entities.Rules
{
public sealed class RuleJobUpdate
{
public string? ExecutionDump { get; set; }
public RuleResult ExecutionResult { get; set; }
public RuleJobResult JobResult { get; set; }
public TimeSpan Elapsed { get; set; }
public Instant Finished { get; set; }
public Instant? JobNext { get; set; }
}
}

47
backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/GrainRuleRunnerService.cs

@ -0,0 +1,47 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Orleans;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Rules.Runner
{
public sealed class GrainRuleRunnerService : IRuleRunnerService
{
private readonly IGrainFactory grainFactory;
public GrainRuleRunnerService(IGrainFactory grainFactory)
{
Guard.NotNull(grainFactory, nameof(grainFactory));
this.grainFactory = grainFactory;
}
public Task CancelAsync(Guid appId)
{
var grain = grainFactory.GetGrain<IRuleRunnerGrain>(appId);
return grain.CancelAsync();
}
public Task<Guid?> GetRunningRuleIdAsync(Guid appId)
{
var grain = grainFactory.GetGrain<IRuleRunnerGrain>(appId);
return grain.GetRunningRuleIdAsync();
}
public Task RunAsync(Guid appId, Guid ruleId)
{
var grain = grainFactory.GetGrain<IRuleRunnerGrain>(appId);
return grain.RunAsync(ruleId);
}
}
}

22
backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/IRuleRunnerGrain.cs

@ -0,0 +1,22 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Orleans;
namespace Squidex.Domain.Apps.Entities.Rules.Runner
{
public interface IRuleRunnerGrain : IGrainWithGuidKey
{
Task RunAsync(Guid ruleId);
Task CancelAsync();
Task<Guid?> GetRunningRuleIdAsync();
}
}

21
backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/IRuleRunnerService.cs

@ -0,0 +1,21 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
namespace Squidex.Domain.Apps.Entities.Rules.Runner
{
public interface IRuleRunnerService
{
Task RunAsync(Guid appId, Guid ruleId);
Task CancelAsync(Guid appId);
Task<Guid?> GetRunningRuleIdAsync(Guid appId);
}
}

202
backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/RuleRunnerGrain.cs

@ -0,0 +1,202 @@
// ==========================================================================
// 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 Orleans;
using Orleans.Runtime;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Entities.Rules.Repositories;
using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Orleans;
using Squidex.Infrastructure.States;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Domain.Apps.Entities.Rules.Runner
{
public sealed class RuleRunnerGrain : GrainOfGuid, IRuleRunnerGrain, IRemindable
{
private readonly IGrainState<State> state;
private readonly IAppProvider appProvider;
private readonly IEventStore eventStore;
private readonly IEventDataFormatter eventDataFormatter;
private readonly IRuleEventRepository ruleEventRepository;
private readonly RuleService ruleService;
private readonly ISemanticLog log;
private CancellationTokenSource? currentTaskToken;
private IGrainReminder? currentReminder;
private bool isStopping;
[CollectionName("Rules_Runner")]
public sealed class State
{
public Guid? RuleId { get; set; }
public string? Position { get; set; }
}
public RuleRunnerGrain(
IGrainState<State> state,
IAppProvider appProvider,
IEventStore eventStore,
IEventDataFormatter eventDataFormatter,
IRuleEventRepository ruleEventRepository,
RuleService ruleService,
ISemanticLog log)
{
Guard.NotNull(state);
Guard.NotNull(appProvider);
Guard.NotNull(eventStore);
Guard.NotNull(eventDataFormatter);
Guard.NotNull(ruleEventRepository);
Guard.NotNull(ruleService);
Guard.NotNull(log);
this.state = state;
this.appProvider = appProvider;
this.eventStore = eventStore;
this.eventDataFormatter = eventDataFormatter;
this.ruleEventRepository = ruleEventRepository;
this.ruleService = ruleService;
this.log = log;
}
protected override Task OnActivateAsync(Guid key)
{
EnsureIsRunning();
return base.OnActivateAsync(key);
}
public override Task OnDeactivateAsync()
{
isStopping = true;
currentTaskToken?.Cancel();
return base.OnDeactivateAsync();
}
public Task CancelAsync()
{
currentTaskToken?.Cancel();
return Task.CompletedTask;
}
public Task<Guid?> GetRunningRuleIdAsync()
{
return Task.FromResult(state.Value.RuleId);
}
public async Task RunAsync(Guid ruleId)
{
if (currentTaskToken != null)
{
throw new DomainException("Another rule is already running.");
}
state.Value = new State
{
RuleId = ruleId
};
EnsureIsRunning();
await state.WriteAsync();
}
private void EnsureIsRunning()
{
if (state.Value.RuleId.HasValue && currentTaskToken == null)
{
currentTaskToken = new CancellationTokenSource();
Process(state.Value, currentTaskToken.Token);
}
}
private void Process(State job, CancellationToken ct)
{
ProcessAsync(job, ct).Forget();
}
private async Task ProcessAsync(State job, CancellationToken ct)
{
try
{
currentReminder = await RegisterOrUpdateReminder("KeepAlive", TimeSpan.Zero, TimeSpan.FromMinutes(2));
var rules = await appProvider.GetRulesAsync(Key);
var rule = rules.Find(x => x.Id == job.RuleId);
if (rule == null)
{
throw new InvalidOperationException("Cannot find rule.");
}
await eventStore.QueryAsync(async storedEvent =>
{
var @event = eventDataFormatter.Parse(storedEvent.Data);
var jobs = await ruleService.CreateJobsAsync(rule.RuleDef, rule.Id, @event);
foreach (var job in jobs)
{
await ruleEventRepository.EnqueueAsync(job, job.Created, ct);
}
job.Position = storedEvent.EventPosition;
await state.WriteAsync();
}, SquidexHeaders.AppId, Key.ToString(), job.Position, ct);
}
catch (OperationCanceledException)
{
return;
}
catch (Exception ex)
{
log.LogError(ex, w => w
.WriteProperty("action", "runeRule")
.WriteProperty("status", "failed")
.WriteProperty("ruleId", job.RuleId?.ToString()));
}
finally
{
if (!isStopping)
{
job.RuleId = null;
job.Position = null;
await state.WriteAsync();
if (currentReminder != null)
{
await UnregisterReminder(currentReminder);
currentReminder = null;
}
currentTaskToken = null;
}
}
}
public Task ReceiveReminder(string reminderName, TickStatus status)
{
EnsureIsRunning();
return Task.CompletedTask;
}
}
}

2
backend/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj

@ -20,7 +20,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="IdentityServer4" Version="3.1.2" /> <PackageReference Include="IdentityServer4" Version="3.1.2" />
<PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" /> <PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
<PackageReference Include="MongoDB.Driver" Version="2.10.2" /> <PackageReference Include="MongoDB.Driver" Version="2.10.3" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" /> <PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" /> <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="System.Security.Principal.Windows" Version="4.7.0" /> <PackageReference Include="System.Security.Principal.Windows" Version="4.7.0" />

1
backend/src/Squidex.Domain.Users/UserManagerExtensions.cs

@ -13,7 +13,6 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Shared.Identity;
namespace Squidex.Domain.Users namespace Squidex.Domain.Users
{ {

1
backend/src/Squidex.Domain.Users/UserValues.cs

@ -51,6 +51,7 @@ namespace Squidex.Domain.Users
void RemoveClaims(Func<Claim, bool> predicate) void RemoveClaims(Func<Claim, bool> predicate)
{ {
claimsToAdd.RemoveAll(x => predicate(x));
claimsToRemove.AddRange(current.Where(predicate)); claimsToRemove.AddRange(current.Where(predicate));
} }

4
backend/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoExtensions.cs

@ -33,11 +33,11 @@ namespace Squidex.Infrastructure.MongoDb
return (await database.ListCollectionNamesAsync(options)).Any(); return (await database.ListCollectionNamesAsync(options)).Any();
} }
public static async Task<bool> InsertOneIfNotExistsAsync<T>(this IMongoCollection<T> collection, T document) public static async Task<bool> InsertOneIfNotExistsAsync<T>(this IMongoCollection<T> collection, T document, CancellationToken ct = default)
{ {
try try
{ {
await collection.InsertOneAsync(document); await collection.InsertOneAsync(document, null, ct);
} }
catch (MongoWriteException ex) catch (MongoWriteException ex)
{ {

4
backend/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj

@ -13,8 +13,8 @@
<ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" /> <ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MongoDB.Driver" Version="2.10.2" /> <PackageReference Include="MongoDB.Driver" Version="2.10.3" />
<PackageReference Include="MongoDB.Driver.GridFS" Version="2.10.2" /> <PackageReference Include="MongoDB.Driver.GridFS" Version="2.10.3" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" /> <PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" /> <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="System.Threading.Tasks.Dataflow" Version="4.11.0" /> <PackageReference Include="System.Threading.Tasks.Dataflow" Version="4.11.0" />

35
backend/src/Squidex.Infrastructure/UniqueConstraintException.cs

@ -1,35 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Runtime.Serialization;
namespace Squidex.Infrastructure
{
[Serializable]
public class UniqueConstraintException : Exception
{
public UniqueConstraintException()
{
}
public UniqueConstraintException(string message)
: base(message)
{
}
public UniqueConstraintException(string message, Exception inner)
: base(message, inner)
{
}
protected UniqueConstraintException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

13
backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleDto.cs

@ -91,7 +91,7 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models
/// </summary> /// </summary>
public Instant? LastExecuted { get; set; } public Instant? LastExecuted { get; set; }
public static RuleDto FromRule(IEnrichedRuleEntity rule, ApiController controller, string app) public static RuleDto FromRule(IEnrichedRuleEntity rule, Guid? runningRuleId, ApiController controller, string app)
{ {
var result = new RuleDto(); var result = new RuleDto();
@ -103,10 +103,10 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models
result.Trigger = RuleTriggerDtoFactory.Create(rule.RuleDef.Trigger); result.Trigger = RuleTriggerDtoFactory.Create(rule.RuleDef.Trigger);
} }
return result.CreateLinks(controller, app); return result.CreateLinks(controller, runningRuleId, app);
} }
private RuleDto CreateLinks(ApiController controller, string app) private RuleDto CreateLinks(ApiController controller, Guid? runningRuleId, string app)
{ {
var values = new { app, id = Id }; var values = new { app, id = Id };
@ -127,9 +127,14 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models
AddPutLink("update", controller.Url<RulesController>(x => nameof(x.PutRule), values)); AddPutLink("update", controller.Url<RulesController>(x => nameof(x.PutRule), values));
} }
if (controller.HasPermission(Permissions.AppRulesRead)) if (controller.HasPermission(Permissions.AppRulesEvents))
{ {
AddPutLink("trigger", controller.Url<RulesController>(x => nameof(x.TriggerRule), values)); AddPutLink("trigger", controller.Url<RulesController>(x => nameof(x.TriggerRule), values));
if (runningRuleId == null)
{
AddPutLink("run", controller.Url<RulesController>(x => nameof(x.PutRuleRun), values));
}
} }
if (controller.HasPermission(Permissions.AppRulesDelete)) if (controller.HasPermission(Permissions.AppRulesDelete))

8
backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs

@ -5,10 +5,10 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using Squidex.Domain.Apps.Entities.Rules; using Squidex.Domain.Apps.Entities.Rules;
using Squidex.Infrastructure;
using Squidex.Web; using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Rules.Models namespace Squidex.Areas.Api.Controllers.Rules.Models
@ -26,12 +26,12 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models
/// </summary> /// </summary>
public long Total { get; set; } public long Total { get; set; }
public static RuleEventsDto FromRuleEvents(IReadOnlyList<IRuleEventEntity> items, long total, ApiController controller, string app) public static RuleEventsDto FromRuleEvents(IResultList<IRuleEventEntity> ruleEvents, ApiController controller, string app)
{ {
var result = new RuleEventsDto var result = new RuleEventsDto
{ {
Total = total, Total = ruleEvents.Total,
Items = items.Select(x => RuleEventDto.FromRuleEvent(x, controller, app)).ToArray() Items = ruleEvents.Select(x => RuleEventDto.FromRuleEvent(x, controller, app)).ToArray()
}; };
return result.CreateLinks(controller, app); return result.CreateLinks(controller, app);

21
backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RulesDto.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
@ -22,17 +23,24 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models
[Required] [Required]
public RuleDto[] Items { get; set; } public RuleDto[] Items { get; set; }
public static RulesDto FromRules(IEnumerable<IEnrichedRuleEntity> items, ApiController controller, string app) /// <summary>
/// The id of the rule that is currently rerunning.
/// </summary>
public Guid? RunningRuleId { get; set; }
public static RulesDto FromRules(IEnumerable<IEnrichedRuleEntity> items, Guid? runningRuleId, ApiController controller, string app)
{ {
var result = new RulesDto var result = new RulesDto
{ {
Items = items.Select(x => RuleDto.FromRule(x, controller, app)).ToArray() Items = items.Select(x => RuleDto.FromRule(x, runningRuleId, controller, app)).ToArray()
}; };
return result.CreateLinks(controller, app); result.RunningRuleId = runningRuleId;
return result.CreateLinks(controller, runningRuleId, app);
} }
private RulesDto CreateLinks(ApiController controller, string app) private RulesDto CreateLinks(ApiController controller, Guid? runningRuleId, string app)
{ {
var values = new { app }; var values = new { app };
@ -46,6 +54,11 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models
if (controller.HasPermission(Permissions.AppRulesEvents, app)) if (controller.HasPermission(Permissions.AppRulesEvents, app))
{ {
AddGetLink("events", controller.Url<RulesController>(x => nameof(x.GetEvents), values)); AddGetLink("events", controller.Url<RulesController>(x => nameof(x.GetEvents), values));
if (runningRuleId != null)
{
AddDeleteLink("run/cancel", controller.Url<RulesController>(x => nameof(x.DeleteRuleRun), values));
}
} }
return this; return this;

61
backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs

@ -17,6 +17,7 @@ using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Entities.Rules; using Squidex.Domain.Apps.Entities.Rules;
using Squidex.Domain.Apps.Entities.Rules.Commands; using Squidex.Domain.Apps.Entities.Rules.Commands;
using Squidex.Domain.Apps.Entities.Rules.Repositories; using Squidex.Domain.Apps.Entities.Rules.Repositories;
using Squidex.Domain.Apps.Entities.Rules.Runner;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Shared; using Squidex.Shared;
@ -31,17 +32,20 @@ namespace Squidex.Areas.Api.Controllers.Rules
public sealed class RulesController : ApiController public sealed class RulesController : ApiController
{ {
private readonly IRuleQueryService ruleQuery; private readonly IRuleQueryService ruleQuery;
private readonly IRuleRunnerService ruleRunnerService;
private readonly IRuleEventRepository ruleEventsRepository; private readonly IRuleEventRepository ruleEventsRepository;
private readonly RuleRegistry ruleRegistry; private readonly RuleRegistry ruleRegistry;
public RulesController(ICommandBus commandBus, public RulesController(ICommandBus commandBus,
IRuleEventRepository ruleEventsRepository, IRuleEventRepository ruleEventsRepository,
IRuleQueryService ruleQuery, IRuleQueryService ruleQuery,
IRuleRunnerService ruleRunnerService,
RuleRegistry ruleRegistry) RuleRegistry ruleRegistry)
: base(commandBus) : base(commandBus)
{ {
this.ruleEventsRepository = ruleEventsRepository; this.ruleEventsRepository = ruleEventsRepository;
this.ruleQuery = ruleQuery; this.ruleQuery = ruleQuery;
this.ruleRunnerService = ruleRunnerService;
this.ruleRegistry = ruleRegistry; this.ruleRegistry = ruleRegistry;
} }
@ -87,9 +91,11 @@ namespace Squidex.Areas.Api.Controllers.Rules
{ {
var rules = await ruleQuery.QueryAsync(Context); var rules = await ruleQuery.QueryAsync(Context);
var runningRuleId = await ruleRunnerService.GetRunningRuleIdAsync(Context.App.Id);
var response = Deferred.Response(() => var response = Deferred.Response(() =>
{ {
return RulesDto.FromRules(rules, this, app); return RulesDto.FromRules(rules, runningRuleId, this, app);
}); });
return Ok(response); return Ok(response);
@ -119,6 +125,25 @@ namespace Squidex.Areas.Api.Controllers.Rules
return CreatedAtAction(nameof(GetRules), new { app }, response); return CreatedAtAction(nameof(GetRules), new { app }, response);
} }
/// <summary>
/// Cancel the current run.
/// </summary>
/// <param name="app">The name of the app.</param>
/// <returns>
/// 204 => Rule run cancelled.
/// </returns>
[HttpDelete]
[Route("apps/{app}/rules/run")]
[ProducesResponseType(204)]
[ApiPermission(Permissions.AppRulesEvents)]
[ApiCosts(1)]
public async Task<IActionResult> DeleteRuleRun(string app)
{
await ruleRunnerService.CancelAsync(App.Id);
return NoContent();
}
/// <summary> /// <summary>
/// Update a rule. /// Update a rule.
/// </summary> /// </summary>
@ -130,9 +155,6 @@ namespace Squidex.Areas.Api.Controllers.Rules
/// 400 => Rule is not valid. /// 400 => Rule is not valid.
/// 404 => Rule or app not found. /// 404 => Rule or app not found.
/// </returns> /// </returns>
/// <remarks>
/// All events for the specified schemas will be sent to the url. The timeout is 2 seconds.
/// </remarks>
[HttpPut] [HttpPut]
[Route("apps/{app}/rules/{id}/")] [Route("apps/{app}/rules/{id}/")]
[ProducesResponseType(typeof(RuleDto), 200)] [ProducesResponseType(typeof(RuleDto), 200)]
@ -215,6 +237,26 @@ namespace Squidex.Areas.Api.Controllers.Rules
return NoContent(); return NoContent();
} }
/// <summary>
/// Run a rule.
/// </summary>
/// <param name="app">The name of the app.</param>
/// <param name="id">The id of the rule to run.</param>
/// <returns>
/// 204 => Rule started.
/// </returns>
[HttpPut]
[Route("apps/{app}/rules/{id}/run")]
[ProducesResponseType(204)]
[ApiPermission(Permissions.AppRulesEvents)]
[ApiCosts(1)]
public async Task<IActionResult> PutRuleRun(string app, Guid id)
{
await ruleRunnerService.RunAsync(App.Id, id);
return NoContent();
}
/// <summary> /// <summary>
/// Delete a rule. /// Delete a rule.
/// </summary> /// </summary>
@ -253,12 +295,9 @@ namespace Squidex.Areas.Api.Controllers.Rules
[ApiCosts(0)] [ApiCosts(0)]
public async Task<IActionResult> GetEvents(string app, [FromQuery] Guid? ruleId = null, [FromQuery] int skip = 0, [FromQuery] int take = 20) public async Task<IActionResult> GetEvents(string app, [FromQuery] Guid? ruleId = null, [FromQuery] int skip = 0, [FromQuery] int take = 20)
{ {
var taskForItems = ruleEventsRepository.QueryByAppAsync(AppId, ruleId, skip, take); var ruleEvents = await ruleEventsRepository.QueryByAppAsync(AppId, ruleId, skip, take);
var taskForCount = ruleEventsRepository.CountByAppAsync(AppId);
await Task.WhenAll(taskForItems, taskForCount);
var response = RuleEventsDto.FromRuleEvents(taskForItems.Result, taskForCount.Result, this, app); var response = RuleEventsDto.FromRuleEvents(ruleEvents, this, app);
return Ok(response); return Ok(response);
} }
@ -321,8 +360,10 @@ namespace Squidex.Areas.Api.Controllers.Rules
{ {
var context = await CommandBus.PublishAsync(command); var context = await CommandBus.PublishAsync(command);
var runningRuleId = await ruleRunnerService.GetRunningRuleIdAsync(Context.App.Id);
var result = context.Result<IEnrichedRuleEntity>(); var result = context.Result<IEnrichedRuleEntity>();
var response = RuleDto.FromRule(result, this, app); var response = RuleDto.FromRule(result, runningRuleId, this, app);
return response; return response;
} }

4
backend/src/Squidex/Config/Domain/RuleServices.cs

@ -15,6 +15,7 @@ using Squidex.Domain.Apps.Entities.Comments;
using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Domain.Apps.Entities.Rules; using Squidex.Domain.Apps.Entities.Rules;
using Squidex.Domain.Apps.Entities.Rules.Queries; using Squidex.Domain.Apps.Entities.Rules.Queries;
using Squidex.Domain.Apps.Entities.Rules.Runner;
using Squidex.Domain.Apps.Entities.Rules.UsageTracking; using Squidex.Domain.Apps.Entities.Rules.UsageTracking;
using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing;
@ -57,6 +58,9 @@ namespace Squidex.Config.Domain
services.AddSingletonAs<RuleQueryService>() services.AddSingletonAs<RuleQueryService>()
.As<IRuleQueryService>(); .As<IRuleQueryService>();
services.AddSingletonAs<GrainRuleRunnerService>()
.As<IRuleRunnerService>();
services.AddSingletonAs<RuleEnricher>() services.AddSingletonAs<RuleEnricher>()
.As<IRuleEnricher>(); .As<IRuleEnricher>();

2
backend/src/Squidex/Squidex.csproj

@ -49,7 +49,7 @@
<PackageReference Include="Microsoft.Orleans.Core" Version="3.1.3" /> <PackageReference Include="Microsoft.Orleans.Core" Version="3.1.3" />
<PackageReference Include="Microsoft.Orleans.Core.Abstractions" Version="3.1.3" /> <PackageReference Include="Microsoft.Orleans.Core.Abstractions" Version="3.1.3" />
<PackageReference Include="Microsoft.Orleans.OrleansRuntime" Version="3.1.3" /> <PackageReference Include="Microsoft.Orleans.OrleansRuntime" Version="3.1.3" />
<PackageReference Include="MongoDB.Driver" Version="2.10.2" /> <PackageReference Include="MongoDB.Driver" Version="2.10.3" />
<PackageReference Include="Namotion.Reflection" Version="1.0.10" /> <PackageReference Include="Namotion.Reflection" Version="1.0.10" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="NJsonSchema" Version="10.1.8" /> <PackageReference Include="NJsonSchema" Version="10.1.8" />

1
backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesTests.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentAssertions; using FluentAssertions;

2
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs

@ -376,7 +376,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
Assert.Equal(now, job.Created); Assert.Equal(now, job.Created);
Assert.Equal(now.Plus(Duration.FromDays(30)), job.Expires); Assert.Equal(now.Plus(Duration.FromDays(30)), job.Expires);
Assert.NotEqual(Guid.Empty, job.EventId); Assert.NotEqual(Guid.Empty, job.Id);
} }
} }
} }

3
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs

@ -10,10 +10,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Squidex.Domain.Apps.Core.Assets;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Json.Objects;
using Xunit; using Xunit;

1
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/AssetsValidatorTests.cs

@ -15,7 +15,6 @@ using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.ValidateContent; using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Domain.Apps.Core.ValidateContent.Validators; using Squidex.Domain.Apps.Core.ValidateContent.Validators;
using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Json.Objects;
using Xunit; using Xunit;
namespace Squidex.Domain.Apps.Core.Operations.ValidateContent.Validators namespace Squidex.Domain.Apps.Core.Operations.ValidateContent.Validators

1
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/ReferencesValidatorTests.cs

@ -11,7 +11,6 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Squidex.Domain.Apps.Core.ValidateContent.Validators; using Squidex.Domain.Apps.Core.ValidateContent.Validators;
using Squidex.Infrastructure.Json.Objects;
using Xunit; using Xunit;
namespace Squidex.Domain.Apps.Core.Operations.ValidateContent namespace Squidex.Domain.Apps.Core.Operations.ValidateContent

11
backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDequeuerTests.cs

@ -126,7 +126,14 @@ namespace Squidex.Domain.Apps.Entities.Rules
await sut.HandleAsync(@event); await sut.HandleAsync(@event);
A.CallTo(() => ruleEventRepository.MarkSentAsync(@event.Job, requestDump, result, jobResult, requestElapsed, now, nextCall)) A.CallTo(() => ruleEventRepository.UpdateAsync(@event.Job,
A<RuleJobUpdate>.That.Matches(x =>
x.Elapsed == requestElapsed &&
x.ExecutionDump == requestDump &&
x.ExecutionResult == result &&
x.Finished == now &&
x.JobNext == nextCall &&
x.JobResult == jobResult)))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -141,7 +148,7 @@ namespace Squidex.Domain.Apps.Entities.Rules
var job = new RuleJob var job = new RuleJob
{ {
EventId = id, Id = id,
ActionData = actionData, ActionData = actionData,
ActionName = actionName, ActionName = actionName,
Created = clock.GetCurrentInstant() Created = clock.GetCurrentInstant()

10
backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs

@ -74,12 +74,12 @@ namespace Squidex.Domain.Apps.Entities.Rules
var job = new RuleJob { Created = now }; var job = new RuleJob { Created = now };
A.CallTo(() => ruleService.CreateJobsAsync(rule.RuleDef, rule.Id, @event)) A.CallTo(() => ruleService.CreateJobsAsync(rule.RuleDef, rule.Id, @event, false))
.Returns(new List<RuleJob> { job }); .Returns(new List<RuleJob> { job });
await sut.Enqueue(rule.RuleDef, rule.Id, @event); await sut.Enqueue(rule.RuleDef, rule.Id, @event);
A.CallTo(() => ruleEventRepository.EnqueueAsync(job, now)) A.CallTo(() => ruleEventRepository.EnqueueAsync(job, now, default))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -96,15 +96,15 @@ namespace Squidex.Domain.Apps.Entities.Rules
A.CallTo(() => appProvider.GetRulesAsync(appId.Id)) A.CallTo(() => appProvider.GetRulesAsync(appId.Id))
.Returns(new List<IRuleEntity> { rule1, rule2 }); .Returns(new List<IRuleEntity> { rule1, rule2 });
A.CallTo(() => ruleService.CreateJobsAsync(rule1.RuleDef, rule1.Id, @event)) A.CallTo(() => ruleService.CreateJobsAsync(rule1.RuleDef, rule1.Id, @event, false))
.Returns(new List<RuleJob> { job1 }); .Returns(new List<RuleJob> { job1 });
A.CallTo(() => ruleService.CreateJobsAsync(rule2.RuleDef, rule2.Id, @event)) A.CallTo(() => ruleService.CreateJobsAsync(rule2.RuleDef, rule2.Id, @event, false))
.Returns(new List<RuleJob>()); .Returns(new List<RuleJob>());
await sut.On(@event); await sut.On(@event);
A.CallTo(() => ruleEventRepository.EnqueueAsync(job1, now)) A.CallTo(() => ruleEventRepository.EnqueueAsync(job1, now, default))
.MustHaveHappened(); .MustHaveHappened();
} }

2
backend/tools/Migrate_00/Migrate_00.csproj

@ -6,7 +6,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MongoDB.Driver" Version="2.10.2" /> <PackageReference Include="MongoDB.Driver" Version="2.10.3" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" /> <PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" /> <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
</ItemGroup> </ItemGroup>

16
frontend/app/app.module.ts

@ -12,22 +12,10 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import {
ApiUrlConfig,
CurrencyConfig,
DecimalSeparatorConfig,
SqxFrameworkModule,
SqxSharedModule,
TitlesConfig,
UIOptions
} from './shared';
import { SqxShellModule } from './shell';
import { routing } from './app.routes'; import { routing } from './app.routes';
import { ApiUrlConfig, CurrencyConfig, DecimalSeparatorConfig, SqxFrameworkModule, SqxSharedModule, TitlesConfig, UIOptions } from './shared';
import { SqxShellModule } from './shell';
export function configApiUrl() { export function configApiUrl() {
const baseElements = document.getElementsByTagName('base'); const baseElements = document.getElementsByTagName('base');

20
frontend/app/app.routes.ts

@ -7,24 +7,8 @@
import { ModuleWithProviders } from '@angular/core'; import { ModuleWithProviders } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { AppMustExistGuard, LoadAppsGuard, MustBeAuthenticatedGuard, MustBeNotAuthenticatedGuard, UnsetAppGuard } from './shared';
import { import { AppAreaComponent, ForbiddenPageComponent, HomePageComponent, InternalAreaComponent, LoginPageComponent, LogoutPageComponent, NotFoundPageComponent } from './shell';
AppAreaComponent,
ForbiddenPageComponent,
HomePageComponent,
InternalAreaComponent,
LoginPageComponent,
LogoutPageComponent,
NotFoundPageComponent
} from './shell';
import {
AppMustExistGuard,
LoadAppsGuard,
MustBeAuthenticatedGuard,
MustBeNotAuthenticatedGuard,
UnsetAppGuard
} from './shared';
export const routes: Routes = [ export const routes: Routes = [
{ {

4
frontend/app/app.ts

@ -5,12 +5,10 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import './theme/theme.scss';
import { enableProdMode } from '@angular/core'; import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module'; import { AppModule } from './app.module';
import './theme/theme.scss';
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === 'production') {
enableProdMode(); enableProdMode();

1
frontend/app/features/administration/administration-area.component.ts

@ -6,7 +6,6 @@
*/ */
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { UIState } from '@app/shared'; import { UIState } from '@app/shared';
@Component({ @Component({

11
frontend/app/features/administration/declarations.ts

@ -6,16 +6,13 @@
*/ */
export * from './administration-area.component'; export * from './administration-area.component';
export * from './guards/user-must-exist.guard';
export * from './guards/unset-user.guard'; export * from './guards/unset-user.guard';
export * from './guards/user-must-exist.guard';
export * from './internal';
export * from './pages/cluster/cluster-page.component'; export * from './pages/cluster/cluster-page.component';
export * from './pages/event-consumers/event-consumer.component'; export * from './pages/event-consumers/event-consumer.component';
export * from './pages/event-consumers/event-consumers-page.component'; export * from './pages/event-consumers/event-consumers-page.component';
export * from './pages/restore/restore-page.component'; export * from './pages/restore/restore-page.component';
export * from './pages/users/user.component';
export * from './pages/users/user-page.component'; export * from './pages/users/user-page.component';
export * from './pages/users/users-page.component'; export * from './pages/users/user.component';
export * from './pages/users/users-page.component';
export * from './internal';

4
frontend/app/features/administration/guards/unset-user.guard.spec.ts

@ -5,11 +5,9 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { UsersState } from '@app/features/administration/internal';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { IMock, Mock, Times } from 'typemoq'; import { IMock, Mock, Times } from 'typemoq';
import { UsersState } from '@app/features/administration/internal';
import { UnsetUserGuard } from './unset-user.guard'; import { UnsetUserGuard } from './unset-user.guard';
describe('UnsetUserGuard', () => { describe('UnsetUserGuard', () => {

3
frontend/app/features/administration/guards/unset-user.guard.ts

@ -7,11 +7,10 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router'; import { CanActivate } from '@angular/router';
import { UsersState } from '@app/features/administration/internal';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { UsersState } from '@app/features/administration/internal';
@Injectable() @Injectable()
export class UnsetUserGuard implements CanActivate { export class UnsetUserGuard implements CanActivate {
constructor( constructor(

4
frontend/app/features/administration/guards/user-must-exist.guard.spec.ts

@ -6,11 +6,9 @@
*/ */
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { UserDto, UsersState } from '@app/features/administration/internal';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { IMock, Mock, Times } from 'typemoq'; import { IMock, Mock, Times } from 'typemoq';
import { UserDto, UsersState } from '@app/features/administration/internal';
import { UserMustExistGuard } from './user-must-exist.guard'; import { UserMustExistGuard } from './user-must-exist.guard';
describe('UserMustExistGuard', () => { describe('UserMustExistGuard', () => {

6
frontend/app/features/administration/guards/user-must-exist.guard.ts

@ -7,13 +7,11 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router'; import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
import { UsersState } from '@app/features/administration/internal';
import { allParams } from '@app/framework';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators'; import { map, tap } from 'rxjs/operators';
import { allParams } from '@app/framework';
import { UsersState } from '@app/features/administration/internal';
@Injectable() @Injectable()
export class UserMustExistGuard implements CanActivate { export class UserMustExistGuard implements CanActivate {
constructor( constructor(

1
frontend/app/features/administration/internal.ts

@ -7,7 +7,6 @@
export * from './services/event-consumers.service'; export * from './services/event-consumers.service';
export * from './services/users.service'; export * from './services/users.service';
export * from './state/event-consumers.state'; export * from './state/event-consumers.state';
export * from './state/users.forms'; export * from './state/users.forms';
export * from './state/users.state'; export * from './state/users.state';

26
frontend/app/features/administration/module.ts

@ -5,30 +5,12 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
// tslint:disable: max-line-length
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { SqxFrameworkModule, SqxSharedModule } from '@app/shared';
import { import { AdministrationAreaComponent, ClusterPageComponent, EventConsumerComponent, EventConsumersPageComponent, EventConsumersService, EventConsumersState, RestorePageComponent, UnsetUserGuard, UserComponent, UserMustExistGuard, UserPageComponent, UsersPageComponent, UsersService, UsersState } from './declarations';
SqxFrameworkModule,
SqxSharedModule
} from '@app/shared';
import {
AdministrationAreaComponent,
ClusterPageComponent,
EventConsumerComponent,
EventConsumersPageComponent,
EventConsumersService,
EventConsumersState,
RestorePageComponent,
UnsetUserGuard,
UserComponent,
UserMustExistGuard,
UserPageComponent,
UsersPageComponent,
UsersService,
UsersState
} from './declarations';
const routes: Routes = [ const routes: Routes = [
{ {

1
frontend/app/features/administration/pages/cluster/cluster-page.component.ts

@ -6,7 +6,6 @@
*/ */
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { UIState } from '@app/shared'; import { UIState } from '@app/shared';
@Component({ @Component({

1
frontend/app/features/administration/pages/event-consumers/event-consumer.component.ts

@ -8,7 +8,6 @@
// tslint:disable: component-selector // tslint:disable: component-selector
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { EventConsumerDto, EventConsumersState } from '@app/features/administration/internal'; import { EventConsumerDto, EventConsumersState } from '@app/features/administration/internal';
@Component({ @Component({

6
frontend/app/features/administration/pages/event-consumers/event-consumers-page.component.ts

@ -6,13 +6,11 @@
*/ */
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { EventConsumerDto, EventConsumersState } from '@app/features/administration/internal';
import { DialogModel, ResourceOwner } from '@app/shared';
import { timer } from 'rxjs'; import { timer } from 'rxjs';
import { switchMap } from 'rxjs/operators'; import { switchMap } from 'rxjs/operators';
import { DialogModel, ResourceOwner } from '@app/shared';
import { EventConsumerDto, EventConsumersState } from '@app/features/administration/internal';
@Component({ @Component({
selector: 'sqx-event-consumers-page', selector: 'sqx-event-consumers-page',
styleUrls: ['./event-consumers-page.component.scss'], styleUrls: ['./event-consumers-page.component.scss'],

9
frontend/app/features/administration/pages/restore/restore-page.component.ts

@ -7,16 +7,9 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms';
import { AuthService, BackupsService, DialogService, RestoreForm, switchSafe } from '@app/shared';
import { timer } from 'rxjs'; import { timer } from 'rxjs';
import {
AuthService,
BackupsService,
DialogService,
RestoreForm,
switchSafe
} from '@app/shared';
@Component({ @Component({
selector: 'sqx-restore-page', selector: 'sqx-restore-page',
styleUrls: ['./restore-page.component.scss'], styleUrls: ['./restore-page.component.scss'],

9
frontend/app/features/administration/pages/users/user-page.component.ts

@ -8,16 +8,9 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { CreateUserDto, UserDto, UserForm, UsersState } from '@app/features/administration/internal';
import { ResourceOwner } from '@app/shared'; import { ResourceOwner } from '@app/shared';
import {
CreateUserDto,
UserDto,
UserForm,
UsersState
} from '@app/features/administration/internal';
@Component({ @Component({
selector: 'sqx-user-page', selector: 'sqx-user-page',
styleUrls: ['./user-page.component.scss'], styleUrls: ['./user-page.component.scss'],

1
frontend/app/features/administration/pages/users/user.component.ts

@ -8,7 +8,6 @@
// tslint:disable: component-selector // tslint:disable: component-selector
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { UserDto, UsersState } from '@app/features/administration/internal'; import { UserDto, UsersState } from '@app/features/administration/internal';
@Component({ @Component({

1
frontend/app/features/administration/pages/users/users-page.component.ts

@ -7,7 +7,6 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms'; import { FormControl } from '@angular/forms';
import { UserDto, UsersState } from '@app/features/administration/internal'; import { UserDto, UsersState } from '@app/features/administration/internal';
@Component({ @Component({

8
frontend/app/features/administration/services/event-consumers.service.spec.ts

@ -7,14 +7,8 @@
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { inject, TestBed } from '@angular/core/testing'; import { inject, TestBed } from '@angular/core/testing';
import { ApiUrlConfig, Resource, ResourceLinks } from '@app/framework'; import { ApiUrlConfig, Resource, ResourceLinks } from '@app/framework';
import { EventConsumerDto, EventConsumersDto, EventConsumersService } from './event-consumers.service';
import {
EventConsumerDto,
EventConsumersDto,
EventConsumersService
} from './event-consumers.service';
describe('EventConsumersService', () => { describe('EventConsumersService', () => {
beforeEach(() => { beforeEach(() => {

9
frontend/app/features/administration/services/event-consumers.service.ts

@ -7,17 +7,10 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ApiUrlConfig, hasAnyLink, pretifyError, Resource, ResourceLinks } from '@app/shared';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import {
ApiUrlConfig,
hasAnyLink,
pretifyError,
Resource,
ResourceLinks
} from '@app/shared';
export class EventConsumersDto { export class EventConsumersDto {
public readonly _links: ResourceLinks; public readonly _links: ResourceLinks;

8
frontend/app/features/administration/services/users.service.spec.ts

@ -7,14 +7,8 @@
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { inject, TestBed } from '@angular/core/testing'; import { inject, TestBed } from '@angular/core/testing';
import { ApiUrlConfig, Resource, ResourceLinks } from '@app/framework'; import { ApiUrlConfig, Resource, ResourceLinks } from '@app/framework';
import { UserDto, UsersDto, UsersService } from './users.service';
import {
UserDto,
UsersDto,
UsersService
} from './users.service';
describe('UsersService', () => { describe('UsersService', () => {
beforeEach(() => { beforeEach(() => {

10
frontend/app/features/administration/services/users.service.ts

@ -7,18 +7,10 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ApiUrlConfig, hasAnyLink, pretifyError, Resource, ResourceLinks, ResultSet } from '@app/shared';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import {
ApiUrlConfig,
hasAnyLink,
pretifyError,
Resource,
ResourceLinks,
ResultSet
} from '@app/shared';
export class UsersDto extends ResultSet<UserDto> { export class UsersDto extends ResultSet<UserDto> {
public get canCreate() { public get canCreate() {
return hasAnyLink(this._links, 'create'); return hasAnyLink(this._links, 'create');

9
frontend/app/features/administration/state/event-consumers.state.spec.ts

@ -5,16 +5,13 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { EventConsumersDto, EventConsumersService } from '@app/features/administration/internal';
import { DialogService } from '@app/framework';
import { of, throwError } from 'rxjs'; import { of, throwError } from 'rxjs';
import { onErrorResumeNext } from 'rxjs/operators'; import { onErrorResumeNext } from 'rxjs/operators';
import { IMock, It, Mock, Times } from 'typemoq'; import { IMock, It, Mock, Times } from 'typemoq';
import { DialogService } from '@app/framework';
import { EventConsumersDto, EventConsumersService } from '@app/features/administration/internal';
import { EventConsumersState } from './event-consumers.state';
import { createEventConsumer } from './../services/event-consumers.service.spec'; import { createEventConsumer } from './../services/event-consumers.service.spec';
import { EventConsumersState } from './event-consumers.state';
describe('EventConsumersState', () => { describe('EventConsumersState', () => {
const eventConsumer1 = createEventConsumer(1); const eventConsumer1 = createEventConsumer(1);

8
frontend/app/features/administration/state/event-consumers.state.ts

@ -6,15 +6,9 @@
*/ */
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { DialogService, shareSubscribed, State } from '@app/shared';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { finalize, tap } from 'rxjs/operators'; import { finalize, tap } from 'rxjs/operators';
import {
DialogService,
shareSubscribed,
State
} from '@app/shared';
import { EventConsumerDto, EventConsumersService } from './../services/event-consumers.service'; import { EventConsumerDto, EventConsumersService } from './../services/event-consumers.service';
interface Snapshot { interface Snapshot {

2
frontend/app/features/administration/state/users.forms.ts

@ -6,9 +6,7 @@
*/ */
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Form, ValidatorsEx } from '@app/shared'; import { Form, ValidatorsEx } from '@app/shared';
import { UpdateUserDto, UserDto } from './../services/users.service'; import { UpdateUserDto, UserDto } from './../services/users.service';
export class UserForm extends Form<FormGroup, UpdateUserDto, UserDto> { export class UserForm extends Form<FormGroup, UpdateUserDto, UserDto> {

18
frontend/app/features/administration/state/users.state.spec.ts

@ -5,25 +5,13 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { UserDto, UsersDto, UsersService } from '@app/features/administration/internal';
import { DialogService, LocalStoreService, Pager } from '@app/shared';
import { of, throwError } from 'rxjs'; import { of, throwError } from 'rxjs';
import { onErrorResumeNext } from 'rxjs/operators'; import { onErrorResumeNext } from 'rxjs/operators';
import { IMock, It, Mock, Times } from 'typemoq'; import { IMock, It, Mock, Times } from 'typemoq';
import {
DialogService,
LocalStoreService,
Pager
} from '@app/shared';
import {
UserDto,
UsersDto,
UsersService
} from '@app/features/administration/internal';
import { UsersState } from './users.state';
import { createUser } from './../services/users.service.spec'; import { createUser } from './../services/users.service.spec';
import { UsersState } from './users.state';
describe('UsersState', () => { describe('UsersState', () => {
const user1 = createUser(1); const user1 = createUser(1);

20
frontend/app/features/administration/state/users.state.ts

@ -6,25 +6,11 @@
*/ */
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import '@app/framework/utils/rxjs-extensions';
import { DialogService, LocalStoreService, Pager, shareSubscribed, State } from '@app/shared';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { catchError, finalize, tap } from 'rxjs/operators'; import { catchError, finalize, tap } from 'rxjs/operators';
import { CreateUserDto, UpdateUserDto, UserDto, UsersService } from './../services/users.service';
import '@app/framework/utils/rxjs-extensions';
import {
DialogService,
LocalStoreService,
Pager,
shareSubscribed,
State
} from '@app/shared';
import {
CreateUserDto,
UpdateUserDto,
UserDto,
UsersService
} from './../services/users.service';
interface Snapshot { interface Snapshot {
// The current users. // The current users.

1
frontend/app/features/api/api-area.component.ts

@ -6,7 +6,6 @@
*/ */
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { AppsState } from '@app/shared'; import { AppsState } from '@app/shared';
@Component({ @Component({

3
frontend/app/features/api/declarations.ts

@ -5,6 +5,5 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
export * from './api-area.component';
export * from './pages/graphql/graphql-page.component'; export * from './pages/graphql/graphql-page.component';
export * from './api-area.component';

2
frontend/app/features/api/index.ts

@ -6,4 +6,4 @@
*/ */
export * from './declarations'; export * from './declarations';
export * from './module'; export * from './module';

12
frontend/app/features/api/module.ts

@ -7,16 +7,8 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { SqxFrameworkModule, SqxSharedModule } from '@app/shared';
import { import { ApiAreaComponent, GraphQLPageComponent } from './declarations';
SqxFrameworkModule,
SqxSharedModule
} from '@app/shared';
import {
ApiAreaComponent,
GraphQLPageComponent
} from './declarations';
const routes: Routes = [ const routes: Routes = [
{ {

11
frontend/app/features/api/pages/graphql/graphql-page.component.ts

@ -6,15 +6,12 @@
*/ */
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core'; import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';
import { of } from 'rxjs'; import { AppsState, GraphQlService } from '@app/shared';
import { catchError } from 'rxjs/operators'; import GraphiQL from 'graphiql';
import * as React from 'react'; import * as React from 'react';
import * as ReactDOM from 'react-dom'; import * as ReactDOM from 'react-dom';
import { of } from 'rxjs';
import GraphiQL from 'graphiql'; import { catchError } from 'rxjs/operators';
import { AppsState, GraphQlService } from '@app/shared';
@Component({ @Component({
selector: 'sqx-graphql-page', selector: 'sqx-graphql-page',

2
frontend/app/features/apps/declarations.ts

@ -7,4 +7,4 @@
export * from './pages/apps-page.component'; export * from './pages/apps-page.component';
export * from './pages/news-dialog.component'; export * from './pages/news-dialog.component';
export * from './pages/onboarding-dialog.component'; export * from './pages/onboarding-dialog.component';

2
frontend/app/features/apps/index.ts

@ -6,4 +6,4 @@
*/ */
export * from './declarations'; export * from './declarations';
export * from './module'; export * from './module';

8
frontend/app/features/apps/module.ts

@ -7,14 +7,8 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { SqxFrameworkModule, SqxSharedModule } from '@app/shared'; import { SqxFrameworkModule, SqxSharedModule } from '@app/shared';
import { AppsPageComponent, NewsDialogComponent, OnboardingDialogComponent } from './declarations';
import {
AppsPageComponent,
NewsDialogComponent,
OnboardingDialogComponent
} from './declarations';
const routes: Routes = [ const routes: Routes = [
{ {

14
frontend/app/features/apps/pages/apps-page.component.ts

@ -6,21 +6,9 @@
*/ */
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { AppDto, AppsState, AuthService, DialogModel, FeatureDto, LocalStoreService, NewsService, OnboardingService, UIOptions, UIState } from '@app/shared';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
import {
AppDto,
AppsState,
AuthService,
DialogModel,
FeatureDto,
LocalStoreService,
NewsService,
OnboardingService,
UIOptions,
UIState
} from '@app/shared';
@Component({ @Component({
selector: 'sqx-apps-page', selector: 'sqx-apps-page',
styleUrls: ['./apps-page.component.scss'], styleUrls: ['./apps-page.component.scss'],

1
frontend/app/features/apps/pages/news-dialog.component.ts

@ -6,7 +6,6 @@
*/ */
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Component, EventEmitter, Input, Output } from '@angular/core';
import { FeatureDto } from '@app/shared'; import { FeatureDto } from '@app/shared';
@Component({ @Component({

1
frontend/app/features/apps/pages/onboarding-dialog.component.ts

@ -6,7 +6,6 @@
*/ */
import { Component, EventEmitter, Output } from '@angular/core'; import { Component, EventEmitter, Output } from '@angular/core';
import { fadeAnimation, slideAnimation } from '@app/framework'; import { fadeAnimation, slideAnimation } from '@app/framework';
@Component({ @Component({

2
frontend/app/features/assets/declarations.ts

@ -5,6 +5,6 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
export * from './pages/asset-tags.component';
export * from './pages/assets-filters-page.component'; export * from './pages/assets-filters-page.component';
export * from './pages/assets-page.component'; export * from './pages/assets-page.component';
export * from './pages/asset-tags.component';

2
frontend/app/features/assets/index.ts

@ -6,4 +6,4 @@
*/ */
export * from './declarations'; export * from './declarations';
export * from './module'; export * from './module';

8
frontend/app/features/assets/module.ts

@ -7,14 +7,8 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { SqxFrameworkModule, SqxSharedModule } from '@app/shared'; import { SqxFrameworkModule, SqxSharedModule } from '@app/shared';
import { AssetsFiltersPageComponent, AssetsPageComponent, AssetTagsComponent } from './declarations';
import {
AssetsFiltersPageComponent,
AssetsPageComponent,
AssetTagsComponent
} from './declarations';
const routes: Routes = [ const routes: Routes = [
{ {

1
frontend/app/features/assets/pages/asset-tags.component.ts

@ -6,7 +6,6 @@
*/ */
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { Tag, TagsSelected } from '@app/shared'; import { Tag, TagsSelected } from '@app/shared';
@Component({ @Component({

8
frontend/app/features/assets/pages/assets-filters-page.component.ts

@ -6,13 +6,7 @@
*/ */
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { AssetsState, Queries, Query, UIState } from '@app/shared';
import {
AssetsState,
Queries,
Query,
UIState
} from '@app/shared';
@Component({ @Component({
selector: 'sqx-assets-filters-page', selector: 'sqx-assets-filters-page',

11
frontend/app/features/assets/pages/assets-page.component.ts

@ -8,16 +8,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms'; import { FormControl } from '@angular/forms';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { AssetsState, DialogModel, LocalStoreService, Queries, Query, ResourceOwner, UIState } from '@app/shared';
import {
AssetsState,
DialogModel,
LocalStoreService,
Queries,
Query,
ResourceOwner,
UIState
} from '@app/shared';
@Component({ @Component({
selector: 'sqx-assets-page', selector: 'sqx-assets-page',

10
frontend/app/features/content/declarations.ts

@ -15,26 +15,22 @@ export * from './pages/contents/contents-filters-page.component';
export * from './pages/contents/contents-page.component'; export * from './pages/contents/contents-page.component';
export * from './pages/contents/custom-view-editor.component'; export * from './pages/contents/custom-view-editor.component';
export * from './pages/schemas/schemas-page.component'; export * from './pages/schemas/schemas-page.component';
export * from './shared/content-status.component'; export * from './shared/content-status.component';
export * from './shared/due-time-selector.component'; export * from './shared/due-time-selector.component';
export * from './shared/preview-button.component';
export * from './shared/forms/array-editor.component'; export * from './shared/forms/array-editor.component';
export * from './shared/forms/array-item.component'; export * from './shared/forms/array-item.component';
export * from './shared/forms/assets-editor.component'; export * from './shared/forms/assets-editor.component';
export * from './shared/forms/field-editor.component'; export * from './shared/forms/field-editor.component';
export * from './shared/forms/stock-photo-editor.component'; export * from './shared/forms/stock-photo-editor.component';
export * from './shared/list/content-list-cell.directive'; export * from './shared/list/content-list-cell.directive';
export * from './shared/list/content-list-field.component'; export * from './shared/list/content-list-field.component';
export * from './shared/list/content-list-header.component'; export * from './shared/list/content-list-header.component';
export * from './shared/list/content-value-editor.component'; export * from './shared/list/content-value-editor.component';
export * from './shared/list/content-value.component'; export * from './shared/list/content-value.component';
export * from './shared/list/content.component'; export * from './shared/list/content.component';
export * from './shared/preview-button.component';
export * from './shared/references/content-creator.component';
export * from './shared/references/content-selector-item.component'; export * from './shared/references/content-selector-item.component';
export * from './shared/references/content-selector.component'; export * from './shared/references/content-selector.component';
export * from './shared/references/content-creator.component';
export * from './shared/references/reference-item.component'; export * from './shared/references/reference-item.component';
export * from './shared/references/references-editor.component'; export * from './shared/references/references-editor.component';

2
frontend/app/features/content/index.ts

@ -6,4 +6,4 @@
*/ */
export * from './declarations'; export * from './declarations';
export * from './module'; export * from './module';

48
frontend/app/features/content/module.ts

@ -5,52 +5,12 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
// tslint:disable: max-line-length
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { CanDeactivateGuard, ContentMustExistGuard, LoadLanguagesGuard, SchemaMustExistPublishedGuard, SchemaMustNotBeSingletonGuard, SqxFrameworkModule, SqxSharedModule, UnsetContentGuard } from '@app/shared';
import { import { ArrayEditorComponent, ArrayItemComponent, AssetsEditorComponent, CommentsPageComponent, ContentComponent, ContentCreatorComponent, ContentEventComponent, ContentFieldComponent, ContentHistoryPageComponent, ContentListCellDirective, ContentListFieldComponent, ContentListHeaderComponent, ContentListWidthPipe, ContentPageComponent, ContentSelectorComponent, ContentSelectorItemComponent, ContentsFiltersPageComponent, ContentsPageComponent, ContentStatusComponent, ContentValueComponent, ContentValueEditorComponent, CustomViewEditorComponent, DueTimeSelectorComponent, FieldEditorComponent, FieldLanguagesComponent, PreviewButtonComponent, ReferenceItemComponent, ReferencesEditorComponent, SchemasPageComponent, StockPhotoEditorComponent } from './declarations';
CanDeactivateGuard,
ContentMustExistGuard,
LoadLanguagesGuard,
SchemaMustExistPublishedGuard,
SchemaMustNotBeSingletonGuard,
SqxFrameworkModule,
SqxSharedModule,
UnsetContentGuard
} from '@app/shared';
import {
ArrayEditorComponent,
ArrayItemComponent,
AssetsEditorComponent,
CommentsPageComponent,
ContentComponent,
ContentCreatorComponent,
ContentEventComponent,
ContentFieldComponent,
ContentHistoryPageComponent,
ContentListCellDirective,
ContentListFieldComponent,
ContentListHeaderComponent,
ContentListWidthPipe,
ContentPageComponent,
ContentSelectorComponent,
ContentSelectorItemComponent,
ContentsFiltersPageComponent,
ContentsPageComponent,
ContentStatusComponent,
ContentValueComponent,
ContentValueEditorComponent,
CustomViewEditorComponent,
DueTimeSelectorComponent,
FieldEditorComponent,
FieldLanguagesComponent,
PreviewButtonComponent,
ReferenceItemComponent,
ReferencesEditorComponent,
SchemasPageComponent,
StockPhotoEditorComponent
} from './declarations';
const routes: Routes = [ const routes: Routes = [
{ {

1
frontend/app/features/content/pages/comments/comments-page.component.ts

@ -7,7 +7,6 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { allParams } from '@app/shared'; import { allParams } from '@app/shared';
@Component({ @Component({

1
frontend/app/features/content/pages/content/content-event.component.ts

@ -6,7 +6,6 @@
*/ */
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
import { ContentDto, HistoryEventDto } from '@app/shared'; import { ContentDto, HistoryEventDto } from '@app/shared';
@Component({ @Component({

15
frontend/app/features/content/pages/content/content-field.component.ts

@ -7,23 +7,10 @@
import { Component, DoCheck, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; import { Component, DoCheck, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { AbstractControl, FormGroup } from '@angular/forms'; import { AbstractControl, FormGroup } from '@angular/forms';
import { AppLanguageDto, AppsState, EditContentForm, fieldInvariant, invalid$, LocalStoreService, RootFieldDto, SchemaDto, TranslationsService, Types, value$ } from '@app/shared';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { combineLatest } from 'rxjs/operators'; import { combineLatest } from 'rxjs/operators';
import {
AppLanguageDto,
AppsState,
EditContentForm,
fieldInvariant,
invalid$,
LocalStoreService,
RootFieldDto,
SchemaDto,
TranslationsService,
Types,
value$
} from '@app/shared';
@Component({ @Component({
selector: 'sqx-content-field', selector: 'sqx-content-field',
styleUrls: ['./content-field.component.scss'], styleUrls: ['./content-field.component.scss'],

16
frontend/app/features/content/pages/content/content-history-page.component.ts

@ -8,23 +8,9 @@
// tslint:disable: triple-equals // tslint:disable: triple-equals
import { Component, OnInit, ViewChild } from '@angular/core'; import { Component, OnInit, ViewChild } from '@angular/core';
import { AppsState, ContentDto, ContentsState, fadeAnimation, HistoryEventDto, HistoryService, ModalModel, ResourceOwner, SchemaDetailsDto, SchemasState, switchSafe } from '@app/shared';
import { Observable, timer } from 'rxjs'; import { Observable, timer } from 'rxjs';
import { filter, map, onErrorResumeNext, switchMap } from 'rxjs/operators'; import { filter, map, onErrorResumeNext, switchMap } from 'rxjs/operators';
import {
AppsState,
ContentDto,
ContentsState,
fadeAnimation,
HistoryEventDto,
HistoryService,
ModalModel,
ResourceOwner,
SchemaDetailsDto,
SchemasState,
switchSafe
} from '@app/shared';
import { DueTimeSelectorComponent } from './../../shared/due-time-selector.component'; import { DueTimeSelectorComponent } from './../../shared/due-time-selector.component';
import { ContentPageComponent } from './content-page.component'; import { ContentPageComponent } from './content-page.component';

25
frontend/app/features/content/pages/content/content-page.component.ts

@ -5,33 +5,14 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
// tslint:disable: max-line-length
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { ApiUrlConfig, AppLanguageDto, AuthService, AutoSaveKey, AutoSaveService, CanComponentDeactivate, ContentDto, ContentsState, DialogService, EditContentForm, fadeAnimation, FieldDto, LanguagesState, ModalModel, ResourceOwner, SchemaDetailsDto, SchemasState, TempService, Version } from '@app/shared';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { debounceTime, filter, onErrorResumeNext, tap } from 'rxjs/operators'; import { debounceTime, filter, onErrorResumeNext, tap } from 'rxjs/operators';
import {
ApiUrlConfig,
AppLanguageDto,
AuthService,
AutoSaveKey,
AutoSaveService,
CanComponentDeactivate,
ContentDto,
ContentsState,
DialogService,
EditContentForm,
fadeAnimation,
FieldDto,
LanguagesState,
ModalModel,
ResourceOwner,
SchemaDetailsDto,
SchemasState,
TempService,
Version
} from '@app/shared';
@Component({ @Component({
selector: 'sqx-content-page', selector: 'sqx-content-page',
styleUrls: ['./content-page.component.scss'], styleUrls: ['./content-page.component.scss'],

1
frontend/app/features/content/pages/content/field-languages.component.ts

@ -6,7 +6,6 @@
*/ */
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { AppLanguageDto, RootFieldDto } from '@app/shared'; import { AppLanguageDto, RootFieldDto } from '@app/shared';
@Component({ @Component({

10
frontend/app/features/content/pages/contents/contents-filters-page.component.ts

@ -6,15 +6,7 @@
*/ */
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ContentsState, Queries, Query, ResourceOwner, SchemasState, UIState } from '@app/shared';
import {
ContentsState,
Queries,
Query,
ResourceOwner,
SchemasState,
UIState
} from '@app/shared';
@Component({ @Component({
selector: 'sqx-contents-filters-page', selector: 'sqx-contents-filters-page',

21
frontend/app/features/content/pages/contents/contents-page.component.ts

@ -7,27 +7,8 @@
import { Component, OnInit, ViewChild } from '@angular/core'; import { Component, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { AppLanguageDto, ContentDto, ContentsState, fadeAnimation, LanguagesState, ModalModel, Queries, Query, QueryModel, queryModelFromSchema, ResourceOwner, SchemaDetailsDto, SchemasState, TableFields, TempService, UIState } from '@app/shared';
import { onErrorResumeNext, switchMap, tap } from 'rxjs/operators'; import { onErrorResumeNext, switchMap, tap } from 'rxjs/operators';
import {
AppLanguageDto,
ContentDto,
ContentsState,
fadeAnimation,
LanguagesState,
ModalModel,
Queries,
Query,
QueryModel,
queryModelFromSchema,
ResourceOwner,
SchemaDetailsDto,
SchemasState,
TableFields,
TempService,
UIState
} from '@app/shared';
import { DueTimeSelectorComponent } from './../../shared/due-time-selector.component'; import { DueTimeSelectorComponent } from './../../shared/due-time-selector.component';
@Component({ @Component({

1
frontend/app/features/content/pages/schemas/schemas-page.component.ts

@ -7,7 +7,6 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms'; import { FormControl } from '@angular/forms';
import { LocalStoreService, SchemaCategory, SchemasState } from '@app/shared'; import { LocalStoreService, SchemaCategory, SchemasState } from '@app/shared';
@Component({ @Component({

1
frontend/app/features/content/shared/content-status.component.ts

@ -6,7 +6,6 @@
*/ */
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { ScheduleDto } from '@app/shared'; import { ScheduleDto } from '@app/shared';
@Component({ @Component({

3
frontend/app/features/content/shared/due-time-selector.component.ts

@ -6,9 +6,8 @@
*/ */
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Observable, of, Subject } from 'rxjs';
import { DialogModel, UIOptions } from '@app/shared'; import { DialogModel, UIOptions } from '@app/shared';
import { Observable, of, Subject } from 'rxjs';
const OPTION_IMMEDIATELY = 'Immediately'; const OPTION_IMMEDIATELY = 'Immediately';

9
frontend/app/features/content/shared/forms/array-editor.component.ts

@ -8,14 +8,7 @@
import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { CdkDragDrop } from '@angular/cdk/drag-drop';
import { ChangeDetectionStrategy, Component, Input, QueryList, ViewChildren } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input, QueryList, ViewChildren } from '@angular/core';
import { AbstractControl, FormArray, FormGroup } from '@angular/forms'; import { AbstractControl, FormArray, FormGroup } from '@angular/forms';
import { AppLanguageDto, EditContentForm, RootFieldDto, sorted } from '@app/shared';
import {
AppLanguageDto,
EditContentForm,
RootFieldDto,
sorted
} from '@app/shared';
import { ArrayItemComponent } from './array-item.component'; import { ArrayItemComponent } from './array-item.component';
@Component({ @Component({

12
frontend/app/features/content/shared/forms/array-item.component.ts

@ -7,18 +7,8 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnDestroy, Output, QueryList, SimpleChanges, ViewChildren } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnDestroy, Output, QueryList, SimpleChanges, ViewChildren } from '@angular/core';
import { AbstractControl, FormGroup } from '@angular/forms'; import { AbstractControl, FormGroup } from '@angular/forms';
import { AppLanguageDto, EditContentForm, FieldDto, FieldFormatter, invalid$, RootFieldDto, value$ } from '@app/shared';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import {
AppLanguageDto,
EditContentForm,
FieldDto,
FieldFormatter,
invalid$,
RootFieldDto,
value$
} from '@app/shared';
import { FieldEditorComponent } from './field-editor.component'; import { FieldEditorComponent } from './field-editor.component';
type FieldControl = { field: FieldDto, control: AbstractControl }; type FieldControl = { field: FieldDto, control: AbstractControl };

13
frontend/app/features/content/shared/forms/assets-editor.component.ts

@ -8,18 +8,7 @@
import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { CdkDragDrop } from '@angular/cdk/drag-drop';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, OnInit } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { AppsState, AssetDto, AssetsService, DialogModel, LocalStoreService, MessageBus, sorted, StatefulControlComponent, Types } from '@app/shared';
import {
AppsState,
AssetDto,
AssetsService,
DialogModel,
LocalStoreService,
MessageBus,
sorted,
StatefulControlComponent,
Types
} from '@app/shared';
export const SQX_ASSETS_EDITOR_CONTROL_VALUE_ACCESSOR: any = { export const SQX_ASSETS_EDITOR_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => AssetsEditorComponent), multi: true provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => AssetsEditorComponent), multi: true

10
frontend/app/features/content/shared/forms/field-editor.component.ts

@ -7,15 +7,7 @@
import { Component, ElementRef, Input, ViewChild } from '@angular/core'; import { Component, ElementRef, Input, ViewChild } from '@angular/core';
import { AbstractControl, FormArray, FormControl } from '@angular/forms'; import { AbstractControl, FormArray, FormControl } from '@angular/forms';
import { AppLanguageDto, EditContentForm, FieldDto, MathHelper, RootFieldDto, Types } from '@app/shared';
import {
AppLanguageDto,
EditContentForm,
FieldDto,
MathHelper,
RootFieldDto,
Types
} from '@app/shared';
@Component({ @Component({
selector: 'sqx-field-editor', selector: 'sqx-field-editor',

10
frontend/app/features/content/shared/forms/stock-photo-editor.component.ts

@ -7,18 +7,10 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, OnInit } from '@angular/core';
import { FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; import { FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';
import { StatefulControlComponent, StockPhotoDto, StockPhotoService, thumbnail, Types, value$ } from '@app/shared';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { debounceTime, map, switchMap, tap } from 'rxjs/operators'; import { debounceTime, map, switchMap, tap } from 'rxjs/operators';
import {
StatefulControlComponent,
StockPhotoDto,
StockPhotoService,
thumbnail,
Types,
value$
} from '@app/shared';
interface State { interface State {
// True when loading assets. // True when loading assets.
isLoading?: boolean; isLoading?: boolean;

8
frontend/app/features/content/shared/list/content-list-cell.directive.ts

@ -6,13 +6,7 @@
*/ */
import { Directive, ElementRef, Input, OnChanges, Pipe, PipeTransform, Renderer2 } from '@angular/core'; import { Directive, ElementRef, Input, OnChanges, Pipe, PipeTransform, Renderer2 } from '@angular/core';
import { MetaFields, RootFieldDto, TableField, Types } from '@app/shared';
import {
MetaFields,
RootFieldDto,
TableField,
Types
} from '@app/shared';
export function getTableWidth(fields: ReadonlyArray<TableField>) { export function getTableWidth(fields: ReadonlyArray<TableField>) {
let result = 0; let result = 0;

11
frontend/app/features/content/shared/list/content-list-field.component.ts

@ -7,16 +7,7 @@
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
import { FormGroup } from '@angular/forms'; import { FormGroup } from '@angular/forms';
import { ContentDto, getContentValue, LanguageDto, MetaFields, RootFieldDto, TableField, Types } from '@app/shared';
import {
ContentDto,
getContentValue,
LanguageDto,
MetaFields,
RootFieldDto,
TableField,
Types
} from '@app/shared';
@Component({ @Component({
selector: 'sqx-content-list-field', selector: 'sqx-content-list-field',

10
frontend/app/features/content/shared/list/content-list-header.component.ts

@ -6,15 +6,7 @@
*/ */
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { LanguageDto, MetaFields, Query, RootFieldDto, TableField, Types } from '@app/shared';
import {
LanguageDto,
MetaFields,
Query,
RootFieldDto,
TableField,
Types
} from '@app/shared';
@Component({ @Component({
selector: 'sqx-content-list-header', selector: 'sqx-content-list-header',

1
frontend/app/features/content/shared/list/content-value-editor.component.ts

@ -7,7 +7,6 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { FormGroup } from '@angular/forms'; import { FormGroup } from '@angular/forms';
import { FieldDto } from '@app/shared'; import { FieldDto } from '@app/shared';
@Component({ @Component({

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save