Browse Source

Merge pull request #240 from Squidex/feature-azure-queue-action

Feature azure queue action
pull/241/head
Sebastian Stehle 8 years ago
committed by GitHub
parent
commit
7b31602700
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. BIN
      Fody.2.3.24.nupkg
  2. BIN
      libs/fody/2.3.24/fody.2.3.24.nupkg
  3. 1
      libs/fody/2.3.24/fody.2.3.24.nupkg.sha512
  4. 17
      libs/fody/2.3.24/fody.nuspec
  5. 4
      src/Squidex.Domain.Apps.Core.Model/FodyWeavers.xml
  6. 18
      src/Squidex.Domain.Apps.Core.Model/Freezable.cs
  7. 46
      src/Squidex.Domain.Apps.Core.Model/Rules/Actions/AlgoliaAction.cs
  8. 24
      src/Squidex.Domain.Apps.Core.Model/Rules/Actions/AzureQueueAction.cs
  9. 31
      src/Squidex.Domain.Apps.Core.Model/Rules/Actions/SlackAction.cs
  10. 31
      src/Squidex.Domain.Apps.Core.Model/Rules/Actions/WebhookAction.cs
  11. 2
      src/Squidex.Domain.Apps.Core.Model/Rules/IRuleActionVisitor.cs
  12. 2
      src/Squidex.Domain.Apps.Core.Model/Rules/RuleAction.cs
  13. 2
      src/Squidex.Domain.Apps.Core.Model/Rules/RuleTrigger.cs
  14. 16
      src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTrigger.cs
  15. 181
      src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs
  16. 31
      src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldProperties.cs
  17. 76
      src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldProperties.cs
  18. 46
      src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs
  19. 16
      src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldProperties.cs
  20. 33
      src/Squidex.Domain.Apps.Core.Model/Schemas/NamedElementPropertiesBase.cs
  21. 76
      src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldProperties.cs
  22. 46
      src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldProperties.cs
  23. 106
      src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs
  24. 31
      src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldProperties.cs
  25. 2
      src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj
  26. 72
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/AzureQueueActionHandler.cs
  27. 13
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/SlackActionHandler.cs
  28. 22
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/WebhookActionHandler.cs
  29. 8
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs
  30. 1
      src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj
  31. 22
      src/Squidex.Domain.Apps.Entities/Rules/Guards/RuleActionValidator.cs
  32. 14
      src/Squidex.Infrastructure/IFreezable.cs
  33. 36
      src/Squidex/Areas/Api/Controllers/Rules/Models/Actions/AzureQueueActionDto.cs
  34. 5
      src/Squidex/Areas/Api/Controllers/Rules/Models/Converters/RuleActionDtoFactory.cs
  35. 1
      src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionDto.cs
  36. 3
      src/Squidex/Config/Domain/ReadServices.cs
  37. 1
      src/Squidex/app/features/rules/declarations.ts
  38. 2
      src/Squidex/app/features/rules/module.ts
  39. 12
      src/Squidex/app/features/rules/pages/rules/actions/algolia-action.component.html
  40. 29
      src/Squidex/app/features/rules/pages/rules/actions/azure-queue-action.component.html
  41. 2
      src/Squidex/app/features/rules/pages/rules/actions/azure-queue-action.component.scss
  42. 61
      src/Squidex/app/features/rules/pages/rules/actions/azure-queue-action.component.ts
  43. 8
      src/Squidex/app/features/rules/pages/rules/actions/slack-action.component.html
  44. 8
      src/Squidex/app/features/rules/pages/rules/actions/webhook-action.component.html
  45. 6
      src/Squidex/app/features/rules/pages/rules/rule-wizard.component.html
  46. 1
      src/Squidex/app/shared/services/rules.service.ts
  47. 5
      src/Squidex/app/theme/_rules.scss
  48. 16
      src/Squidex/app/theme/icomoon/demo.html
  49. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.eot
  50. 2
      src/Squidex/app/theme/icomoon/fonts/icomoon.svg
  51. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.ttf
  52. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.woff
  53. 2
      src/Squidex/app/theme/icomoon/selection.json
  54. 13
      src/Squidex/app/theme/icomoon/style.css
  55. 31
      tests/Squidex.Domain.Apps.Core.Tests/Model/Rules/RuleTests.cs
  56. 64
      tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaFieldTests.cs
  57. 1
      tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs
  58. 2
      tests/Squidex.Domain.Apps.Core.Tests/TestData.cs
  59. 56
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Actions/AzureQueueActionTests.cs

BIN
Fody.2.3.24.nupkg

Binary file not shown.

BIN
libs/fody/2.3.24/fody.2.3.24.nupkg

Binary file not shown.

1
libs/fody/2.3.24/fody.2.3.24.nupkg.sha512

@ -0,0 +1 @@
tTFEibLh222u4a7SJ+I4zCkfPRBRF4mHF446OSRQtILNZ2tH0Te/rSM7MCt1FzvLAy5KeNlZHfwdlS+AJZDa6w==

17
libs/fody/2.3.24/fody.nuspec

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Fody</id>
<version>2.3.24</version>
<authors>Simon Cropp</authors>
<owners>Simon Cropp</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<developmentDependency>true</developmentDependency>
<licenseUrl>http://www.opensource.org/licenses/mit-license.php</licenseUrl>
<projectUrl>http://github.com/Fody/Fody</projectUrl>
<iconUrl>https://raw.github.com/Fody/Fody/master/package_icon.png</iconUrl>
<description>Extensible tool for weaving .net assemblies.</description>
<copyright></copyright>
<tags>ILWeaving, Fody, Cecil, AOP</tags>
</metadata>
</package>

4
src/Squidex.Domain.Apps.Core.Model/FodyWeavers.xml

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?>
<Weavers>
<Freezable/>
</Weavers>

18
src/Squidex.Infrastructure/Freezable.cs → src/Squidex.Domain.Apps.Core.Model/Freezable.cs

@ -6,16 +6,22 @@
// ==========================================================================
using System;
using Squidex.Infrastructure;
namespace Squidex.Infrastructure
namespace Squidex.Domain.Apps.Core
{
public abstract class Freezable
public abstract class Freezable : IFreezable
{
public bool IsFrozen { get; private set; }
private bool isFrozen;
protected void ThrowIfFrozen()
public bool IsFrozen
{
if (IsFrozen)
get { return isFrozen; }
}
protected void CheckIfFrozen()
{
if (isFrozen)
{
throw new InvalidOperationException("Object is frozen");
}
@ -23,7 +29,7 @@ namespace Squidex.Infrastructure
public void Freeze()
{
IsFrozen = true;
isFrozen = true;
}
}
}

46
src/Squidex.Domain.Apps.Core.Model/Rules/Actions/AlgoliaAction.cs

@ -12,51 +12,11 @@ namespace Squidex.Domain.Apps.Core.Rules.Actions
[TypeName(nameof(AlgoliaAction))]
public sealed class AlgoliaAction : RuleAction
{
private string appId;
private string apiKey;
private string indexName;
public string AppId { get; set; }
public string AppId
{
get
{
return appId;
}
set
{
ThrowIfFrozen();
appId = value;
}
}
public string ApiKey
{
get
{
return apiKey;
}
set
{
ThrowIfFrozen();
public string ApiKey { get; set; }
apiKey = value;
}
}
public string IndexName
{
get
{
return indexName;
}
set
{
ThrowIfFrozen();
indexName = value;
}
}
public string IndexName { get; set; }
public override T Accept<T>(IRuleActionVisitor<T> visitor)
{

24
src/Squidex.Domain.Apps.Core.Model/Rules/Actions/AzureQueueAction.cs

@ -0,0 +1,24 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Rules.Actions
{
[TypeName(nameof(AzureQueueAction))]
public sealed class AzureQueueAction : RuleAction
{
public string ConnectionString { get; set; }
public string Queue { get; set; }
public override T Accept<T>(IRuleActionVisitor<T> visitor)
{
return visitor.Visit(this);
}
}
}

31
src/Squidex.Domain.Apps.Core.Model/Rules/Actions/SlackAction.cs

@ -13,36 +13,9 @@ namespace Squidex.Domain.Apps.Core.Rules.Actions
[TypeName(nameof(SlackAction))]
public sealed class SlackAction : RuleAction
{
private Uri webhookUrl;
private string text;
public Uri WebhookUrl { get; set; }
public Uri WebhookUrl
{
get
{
return webhookUrl;
}
set
{
ThrowIfFrozen();
webhookUrl = value;
}
}
public string Text
{
get
{
return text;
}
set
{
ThrowIfFrozen();
text = value;
}
}
public string Text { get; set; }
public override T Accept<T>(IRuleActionVisitor<T> visitor)
{

31
src/Squidex.Domain.Apps.Core.Model/Rules/Actions/WebhookAction.cs

@ -13,36 +13,9 @@ namespace Squidex.Domain.Apps.Core.Rules.Actions
[TypeName(nameof(WebhookAction))]
public sealed class WebhookAction : RuleAction
{
private Uri url;
private string sharedSecret;
public Uri Url { get; set; }
public Uri Url
{
get
{
return url;
}
set
{
ThrowIfFrozen();
url = value;
}
}
public string SharedSecret
{
get
{
return sharedSecret;
}
set
{
ThrowIfFrozen();
sharedSecret = value;
}
}
public string SharedSecret { get; set; }
public override T Accept<T>(IRuleActionVisitor<T> visitor)
{

2
src/Squidex.Domain.Apps.Core.Model/Rules/IRuleActionVisitor.cs

@ -13,6 +13,8 @@ namespace Squidex.Domain.Apps.Core.Rules
{
T Visit(AlgoliaAction action);
T Visit(AzureQueueAction action);
T Visit(SlackAction action);
T Visit(WebhookAction action);

2
src/Squidex.Domain.Apps.Core.Model/Rules/RuleAction.cs

@ -5,8 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Rules
{
public abstract class RuleAction : Freezable

2
src/Squidex.Domain.Apps.Core.Model/Rules/RuleTrigger.cs

@ -5,8 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Rules
{
public abstract class RuleTrigger : Freezable

16
src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTrigger.cs

@ -13,21 +13,7 @@ namespace Squidex.Domain.Apps.Core.Rules.Triggers
[TypeName(nameof(ContentChangedTrigger))]
public sealed class ContentChangedTrigger : RuleTrigger
{
private ImmutableList<ContentChangedTriggerSchema> schemas;
public ImmutableList<ContentChangedTriggerSchema> Schemas
{
get
{
return schemas;
}
set
{
ThrowIfFrozen();
schemas = value;
}
}
public ImmutableList<ContentChangedTriggerSchema> Schemas { get; set; }
public override T Accept<T>(IRuleTriggerVisitor<T> visitor)
{

181
src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs

@ -13,186 +13,29 @@ namespace Squidex.Domain.Apps.Core.Schemas
[TypeName(nameof(AssetsField))]
public sealed class AssetsFieldProperties : FieldProperties
{
private bool mustBeImage;
private int? minItems;
private int? maxItems;
private int? minWidth;
private int? maxWidth;
private int? minHeight;
private int? maxHeight;
private int? minSize;
private int? maxSize;
private int? aspectWidth;
private int? aspectHeight;
private ImmutableList<string> allowedExtensions;
public bool MustBeImage { get; set; }
public bool MustBeImage
{
get
{
return mustBeImage;
}
set
{
ThrowIfFrozen();
public int? MinItems { get; set; }
mustBeImage = value;
}
}
public int? MaxItems { get; set; }
public int? MinItems
{
get
{
return minItems;
}
set
{
ThrowIfFrozen();
public int? MinWidth { get; set; }
minItems = value;
}
}
public int? MaxWidth { get; set; }
public int? MaxItems
{
get
{
return maxItems;
}
set
{
ThrowIfFrozen();
public int? MinHeight { get; set; }
maxItems = value;
}
}
public int? MaxHeight { get; set; }
public int? MinWidth
{
get
{
return minWidth;
}
set
{
ThrowIfFrozen();
public int? MinSize { get; set; }
minWidth = value;
}
}
public int? MaxSize { get; set; }
public int? MaxWidth
{
get
{
return maxWidth;
}
set
{
ThrowIfFrozen();
public int? AspectWidth { get; set; }
maxWidth = value;
}
}
public int? AspectHeight { get; set; }
public int? MinHeight
{
get
{
return minHeight;
}
set
{
ThrowIfFrozen();
minHeight = value;
}
}
public int? MaxHeight
{
get
{
return maxHeight;
}
set
{
ThrowIfFrozen();
maxHeight = value;
}
}
public int? MinSize
{
get
{
return minSize;
}
set
{
ThrowIfFrozen();
minSize = value;
}
}
public int? MaxSize
{
get
{
return maxSize;
}
set
{
ThrowIfFrozen();
maxSize = value;
}
}
public int? AspectWidth
{
get
{
return aspectWidth;
}
set
{
ThrowIfFrozen();
aspectWidth = value;
}
}
public int? AspectHeight
{
get
{
return aspectHeight;
}
set
{
ThrowIfFrozen();
aspectHeight = value;
}
}
public ImmutableList<string> AllowedExtensions
{
get
{
return allowedExtensions;
}
set
{
ThrowIfFrozen();
allowedExtensions = value;
}
}
public ImmutableList<string> AllowedExtensions { get; set; }
public override T Accept<T>(IFieldPropertiesVisitor<T> visitor)
{

31
src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldProperties.cs

@ -12,36 +12,9 @@ namespace Squidex.Domain.Apps.Core.Schemas
[TypeName(nameof(BooleanField))]
public sealed class BooleanFieldProperties : FieldProperties
{
private BooleanFieldEditor editor;
private bool? defaultValue;
public bool? DefaultValue { get; set; }
public bool? DefaultValue
{
get
{
return defaultValue;
}
set
{
ThrowIfFrozen();
defaultValue = value;
}
}
public BooleanFieldEditor Editor
{
get
{
return editor;
}
set
{
ThrowIfFrozen();
editor = value;
}
}
public BooleanFieldEditor Editor { get; set; }
public override T Accept<T>(IFieldPropertiesVisitor<T> visitor)
{

76
src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldProperties.cs

@ -13,81 +13,15 @@ namespace Squidex.Domain.Apps.Core.Schemas
[TypeName(nameof(DateTimeField))]
public sealed class DateTimeFieldProperties : FieldProperties
{
private DateTimeFieldEditor editor;
private DateTimeCalculatedDefaultValue? calculatedDefaultValue;
private Instant? maxValue;
private Instant? minValue;
private Instant? defaultValue;
public Instant? MaxValue { get; set; }
public Instant? MaxValue
{
get
{
return maxValue;
}
set
{
ThrowIfFrozen();
maxValue = value;
}
}
public Instant? MinValue
{
get
{
return minValue;
}
set
{
ThrowIfFrozen();
minValue = value;
}
}
public Instant? DefaultValue
{
get
{
return defaultValue;
}
set
{
ThrowIfFrozen();
defaultValue = value;
}
}
public DateTimeCalculatedDefaultValue? CalculatedDefaultValue
{
get
{
return calculatedDefaultValue;
}
set
{
ThrowIfFrozen();
public Instant? MinValue { get; set; }
calculatedDefaultValue = value;
}
}
public Instant? DefaultValue { get; set; }
public DateTimeFieldEditor Editor
{
get
{
return editor;
}
set
{
ThrowIfFrozen();
public DateTimeCalculatedDefaultValue? CalculatedDefaultValue { get; set; }
editor = value;
}
}
public DateTimeFieldEditor Editor { get; set; }
public override T Accept<T>(IFieldPropertiesVisitor<T> visitor)
{

46
src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs

@ -9,51 +9,11 @@ namespace Squidex.Domain.Apps.Core.Schemas
{
public abstract class FieldProperties : NamedElementPropertiesBase
{
private bool isRequired;
private bool isListField;
private string placeholder;
public bool IsRequired { get; set; }
public bool IsRequired
{
get
{
return isRequired;
}
set
{
ThrowIfFrozen();
public bool IsListField { get; set; }
isRequired = value;
}
}
public bool IsListField
{
get
{
return isListField;
}
set
{
ThrowIfFrozen();
isListField = value;
}
}
public string Placeholder
{
get
{
return placeholder;
}
set
{
ThrowIfFrozen();
placeholder = value;
}
}
public string Placeholder { get; set; }
public abstract T Accept<T>(IFieldPropertiesVisitor<T> visitor);

16
src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldProperties.cs

@ -12,21 +12,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
[TypeName(nameof(GeolocationField))]
public sealed class GeolocationFieldProperties : FieldProperties
{
private GeolocationFieldEditor editor;
public GeolocationFieldEditor Editor
{
get
{
return editor;
}
set
{
ThrowIfFrozen();
editor = value;
}
}
public GeolocationFieldEditor Editor { get; set; }
public override T Accept<T>(IFieldPropertiesVisitor<T> visitor)
{

33
src/Squidex.Domain.Apps.Core.Model/Schemas/NamedElementPropertiesBase.cs

@ -5,41 +5,12 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Schemas
{
public abstract class NamedElementPropertiesBase : Freezable
{
private string label;
private string hints;
public string Label
{
get
{
return label;
}
set
{
ThrowIfFrozen();
label = value;
}
}
public string Hints
{
get
{
return hints;
}
set
{
ThrowIfFrozen();
public string Label { get; set; }
hints = value;
}
}
public string Hints { get; set; }
}
}

76
src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldProperties.cs

@ -13,81 +13,15 @@ namespace Squidex.Domain.Apps.Core.Schemas
[TypeName(nameof(NumberField))]
public sealed class NumberFieldProperties : FieldProperties
{
private double? maxValue;
private double? minValue;
private double? defaultValue;
private ImmutableList<double> allowedValues;
private NumberFieldEditor editor;
public double? MaxValue { get; set; }
public double? MaxValue
{
get
{
return maxValue;
}
set
{
ThrowIfFrozen();
maxValue = value;
}
}
public double? MinValue
{
get
{
return minValue;
}
set
{
ThrowIfFrozen();
minValue = value;
}
}
public double? DefaultValue
{
get
{
return defaultValue;
}
set
{
ThrowIfFrozen();
defaultValue = value;
}
}
public ImmutableList<double> AllowedValues
{
get
{
return allowedValues;
}
set
{
ThrowIfFrozen();
public double? MinValue { get; set; }
allowedValues = value;
}
}
public double? DefaultValue { get; set; }
public NumberFieldEditor Editor
{
get
{
return editor;
}
set
{
ThrowIfFrozen();
public ImmutableList<double> AllowedValues { get; set; }
editor = value;
}
}
public NumberFieldEditor Editor { get; set; }
public override T Accept<T>(IFieldPropertiesVisitor<T> visitor)
{

46
src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldProperties.cs

@ -13,51 +13,11 @@ namespace Squidex.Domain.Apps.Core.Schemas
[TypeName(nameof(ReferencesField))]
public sealed class ReferencesFieldProperties : FieldProperties
{
private int? minItems;
private int? maxItems;
private Guid schemaId;
public int? MinItems { get; set; }
public int? MinItems
{
get
{
return minItems;
}
set
{
ThrowIfFrozen();
minItems = value;
}
}
public int? MaxItems
{
get
{
return maxItems;
}
set
{
ThrowIfFrozen();
public int? MaxItems { get; set; }
maxItems = value;
}
}
public Guid SchemaId
{
get
{
return schemaId;
}
set
{
ThrowIfFrozen();
schemaId = value;
}
}
public Guid SchemaId { get; set; }
public override T Accept<T>(IFieldPropertiesVisitor<T> visitor)
{

106
src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs

@ -13,111 +13,19 @@ namespace Squidex.Domain.Apps.Core.Schemas
[TypeName(nameof(StringField))]
public sealed class StringFieldProperties : FieldProperties
{
private int? minLength;
private int? maxLength;
private string pattern;
private string patternMessage;
private string defaultValue;
private ImmutableList<string> allowedValues;
private StringFieldEditor editor;
public int? MinLength { get; set; }
public int? MinLength
{
get
{
return minLength;
}
set
{
ThrowIfFrozen();
minLength = value;
}
}
public int? MaxLength
{
get
{
return maxLength;
}
set
{
ThrowIfFrozen();
maxLength = value;
}
}
public string DefaultValue
{
get
{
return defaultValue;
}
set
{
ThrowIfFrozen();
defaultValue = value;
}
}
public int? MaxLength { get; set; }
public string Pattern
{
get
{
return pattern;
}
set
{
ThrowIfFrozen();
pattern = value;
}
}
public string DefaultValue { get; set; }
public string PatternMessage
{
get
{
return patternMessage;
}
set
{
ThrowIfFrozen();
public string Pattern { get; set; }
patternMessage = value;
}
}
public string PatternMessage { get; set; }
public ImmutableList<string> AllowedValues
{
get
{
return allowedValues;
}
set
{
ThrowIfFrozen();
public ImmutableList<string> AllowedValues { get; set; }
allowedValues = value;
}
}
public StringFieldEditor Editor
{
get
{
return editor;
}
set
{
ThrowIfFrozen();
editor = value;
}
}
public StringFieldEditor Editor { get; set; }
public override T Accept<T>(IFieldPropertiesVisitor<T> visitor)
{

31
src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldProperties.cs

@ -12,36 +12,9 @@ namespace Squidex.Domain.Apps.Core.Schemas
[TypeName(nameof(TagsField))]
public sealed class TagsFieldProperties : FieldProperties
{
private int? minItems;
private int? maxItems;
public int? MinItems { get; set; }
public int? MinItems
{
get
{
return minItems;
}
set
{
ThrowIfFrozen();
minItems = value;
}
}
public int? MaxItems
{
get
{
return maxItems;
}
set
{
ThrowIfFrozen();
maxItems = value;
}
}
public int? MaxItems { get; set; }
public override T Accept<T>(IFieldPropertiesVisitor<T> visitor)
{

2
src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj

@ -8,6 +8,8 @@
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Fody" Version="2.3.24" />
<PackageReference Include="Freezable.Fody" Version="1.8.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.4.0" />
<PackageReference Include="System.ValueTuple" Version="4.4.0" />
</ItemGroup>

72
src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/AzureQueueActionHandler.cs

@ -0,0 +1,72 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Queue;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Core.Rules.Actions;
using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Core.HandleRules.Actions
{
public sealed class AzureQueueActionHandler : RuleActionHandler<AzureQueueAction>
{
private readonly ConcurrentDictionary<(string ConnectionString, string QueueName), CloudQueue> queues = new ConcurrentDictionary<(string ConnectionString, string QueueName), CloudQueue>();
private readonly RuleEventFormatter formatter;
public AzureQueueActionHandler(RuleEventFormatter formatter)
{
Guard.NotNull(formatter, nameof(formatter));
this.formatter = formatter;
}
protected override (string Description, RuleJobData Data) CreateJob(Envelope<AppEvent> @event, string eventName, AzureQueueAction action)
{
var body = formatter.ToRouteData(@event, eventName);
var ruleDescription = $"Send event to azure queue '{action.Queue}'";
var ruleData = new RuleJobData
{
["QueueConnectionString"] = action.ConnectionString,
["QueueName"] = action.Queue,
["MessageBody"] = body
};
return (ruleDescription, ruleData);
}
public override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(RuleJobData job)
{
var queueConnectionString = job["QueueConnectionString"].Value<string>();
var queueName = job["QueueName"].Value<string>();
var queue = queues.GetOrAdd((queueConnectionString, queueName), s =>
{
var storageAccount = CloudStorageAccount.Parse(queueConnectionString);
var queueClient = storageAccount.CreateCloudQueueClient();
var queueRef = queueClient.GetQueueReference(queueName);
return queueRef;
});
var messageBody = job["MessageBody"].ToString(Formatting.Indented);
await queue.AddMessageAsync(new CloudQueueMessage(messageBody));
return ("Completed", null);
}
}
}

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

@ -49,31 +49,30 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
private JObject CreatePayload(Envelope<AppEvent> @event, string text)
{
return new JObject(
new JProperty("text", formatter.FormatString(text, @event)));
return new JObject(new JProperty("text", formatter.FormatString(text, @event)));
}
public override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(RuleJobData job)
{
var requestBody = job["RequestBody"].ToString(Formatting.Indented);
var request = BuildRequest(job, requestBody);
var requestMsg = BuildRequest(job, requestBody);
HttpResponseMessage response = null;
try
{
response = await Client.SendAsync(request);
response = await Client.SendAsync(requestMsg);
var responseString = await response.Content.ReadAsStringAsync();
var requestDump = DumpFormatter.BuildDump(request, response, requestBody, responseString, TimeSpan.Zero, false);
var requestDump = DumpFormatter.BuildDump(requestMsg, response, requestBody, responseString, TimeSpan.Zero, false);
return (requestDump, null);
}
catch (Exception ex)
{
if (request != null)
if (requestMsg != null)
{
var requestDump = DumpFormatter.BuildDump(request, response, requestBody, ex.ToString(), TimeSpan.Zero, false);
var requestDump = DumpFormatter.BuildDump(requestMsg, response, requestBody, ex.ToString(), TimeSpan.Zero, false);
return (requestDump, ex);
}

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

@ -35,11 +35,11 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
protected override (string Description, RuleJobData Data) CreateJob(Envelope<AppEvent> @event, string eventName, WebhookAction action)
{
var body = CreatePayload(@event, eventName);
var body = formatter.ToRouteData(@event, eventName);
var signature = $"{body.ToString(Formatting.Indented)}{action.SharedSecret}".Sha256Base64();
var ruleDescription = $"Send event to webhook {action.Url}";
var ruleDescription = $"Send event to webhook '{action.Url}'";
var ruleData = new RuleJobData
{
["RequestUrl"] = action.Url,
@ -50,18 +50,10 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
return (ruleDescription, ruleData);
}
private JObject CreatePayload(Envelope<AppEvent> @event, string eventName)
{
return new JObject(
new JProperty("type", eventName),
new JProperty("payload", formatter.ToRouteData(@event.Payload)),
new JProperty("timestamp", @event.Headers.Timestamp().ToString()));
}
public override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(RuleJobData job)
{
var requestBody = job["RequestBody"].ToString(Formatting.Indented);
var request = BuildRequest(job, requestBody);
var requestMsg = BuildRequest(job, requestBody);
HttpResponseMessage response = null;
@ -69,19 +61,19 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
{
using (var client = new HttpClient { Timeout = Timeout })
{
response = await client.SendAsync(request);
response = await client.SendAsync(requestMsg);
var responseString = await response.Content.ReadAsStringAsync();
var requestDump = DumpFormatter.BuildDump(request, response, requestBody, responseString, TimeSpan.Zero, false);
var requestDump = DumpFormatter.BuildDump(requestMsg, response, requestBody, responseString, TimeSpan.Zero, false);
return (requestDump, null);
}
}
catch (Exception ex)
{
if (request != null)
if (requestMsg != null)
{
var requestDump = DumpFormatter.BuildDump(request, response, requestBody, ex.ToString(), TimeSpan.Zero, false);
var requestDump = DumpFormatter.BuildDump(requestMsg, response, requestBody, ex.ToString(), TimeSpan.Zero, false);
return (requestDump, ex);
}

8
src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs

@ -43,6 +43,14 @@ namespace Squidex.Domain.Apps.Core.HandleRules
return JToken.FromObject(value, serializer);
}
public virtual JToken ToRouteData(Envelope<AppEvent> @event, string eventName)
{
return new JObject(
new JProperty("type", eventName),
new JProperty("payload", JToken.FromObject(@event.Payload, serializer)),
new JProperty("timestamp", @event.Headers.Timestamp().ToString()));
}
public virtual string FormatString(string text, Envelope<AppEvent> @event)
{
var sb = new StringBuilder(text);

1
src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj

@ -23,6 +23,7 @@
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2" />
<PackageReference Include="System.Collections.Immutable" Version="1.4.0" />
<PackageReference Include="System.ValueTuple" Version="4.4.0" />
<PackageReference Include="WindowsAzure.Storage" Version="8.7.0" />
</ItemGroup>
<PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>

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

@ -6,6 +6,7 @@
// ==========================================================================
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Core.Rules.Actions;
@ -46,6 +47,27 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards
return Task.FromResult<IEnumerable<ValidationError>>(errors);
}
public Task<IEnumerable<ValidationError>> Visit(AzureQueueAction action)
{
var errors = new List<ValidationError>();
if (string.IsNullOrWhiteSpace(action.ConnectionString))
{
errors.Add(new ValidationError("Connection string must be defined.", nameof(action.ConnectionString)));
}
if (string.IsNullOrWhiteSpace(action.Queue))
{
errors.Add(new ValidationError("Queue must be defined.", nameof(action.Queue)));
}
else if (!Regex.IsMatch(action.Queue, "^[a-z][a-z0-9]{2,}(\\-[a-z0-9]+)*$"))
{
errors.Add(new ValidationError("Queue must be valid azure queue name.", nameof(action.Queue)));
}
return Task.FromResult<IEnumerable<ValidationError>>(errors);
}
public Task<IEnumerable<ValidationError>> Visit(SlackAction action)
{
var errors = new List<ValidationError>();

14
src/Squidex.Infrastructure/IFreezable.cs

@ -0,0 +1,14 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Infrastructure
{
public interface IFreezable
{
void Freeze();
}
}

36
src/Squidex/Areas/Api/Controllers/Rules/Models/Actions/AzureQueueActionDto.cs

@ -0,0 +1,36 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.ComponentModel.DataAnnotations;
using NJsonSchema.Annotations;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Core.Rules.Actions;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Rules.Models.Actions
{
[JsonSchema("AzureQueue")]
public class AzureQueueActionDto : RuleActionDto
{
/// <summary>
/// The connection string to the storage account.
/// </summary>
[Required]
public string ConnectionString { get; set; }
/// <summary>
/// The queue name.
/// </summary>
[Required]
public string Queue { get; set; }
public override RuleAction ToAction()
{
return SimpleMapper.Map(this, new AzureQueueAction());
}
}
}

5
src/Squidex/Areas/Api/Controllers/Rules/Models/Converters/RuleActionDtoFactory.cs

@ -30,6 +30,11 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models.Converters
return SimpleMapper.Map(action, new AlgoliaActionDto());
}
public RuleActionDto Visit(AzureQueueAction action)
{
return SimpleMapper.Map(action, new AzureQueueActionDto());
}
public RuleActionDto Visit(SlackAction action)
{
return SimpleMapper.Map(action, new SlackActionDto());

1
src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionDto.cs

@ -14,6 +14,7 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models
{
[JsonConverter(typeof(JsonInheritanceConverter), "actionType")]
[KnownType(typeof(AlgoliaActionDto))]
[KnownType(typeof(AzureQueueActionDto))]
[KnownType(typeof(SlackActionDto))]
[KnownType(typeof(WebhookActionDto))]
public abstract class RuleActionDto

3
src/Squidex/Config/Domain/ReadServices.cs

@ -98,6 +98,9 @@ namespace Squidex.Config.Domain
services.AddSingletonAs<AlgoliaActionHandler>()
.As<IRuleActionHandler>();
services.AddSingletonAs<AzureQueueActionHandler>()
.As<IRuleActionHandler>();
services.AddSingletonAs<SlackActionHandler>()
.As<IRuleActionHandler>();

1
src/Squidex/app/features/rules/declarations.ts

@ -6,6 +6,7 @@
*/
export * from './pages/rules/actions/algolia-action.component';
export * from './pages/rules/actions/azure-queue-action.component';
export * from './pages/rules/actions/slack-action.component';
export * from './pages/rules/actions/webhook-action.component';
export * from './pages/rules/triggers/content-changed-trigger.component';

2
src/Squidex/app/features/rules/module.ts

@ -16,6 +16,7 @@ import {
import {
AlgoliaActionComponent,
AzureQueueActionComponent,
ContentChangedTriggerComponent,
RuleEventsPageComponent,
RulesPageComponent,
@ -52,6 +53,7 @@ const routes: Routes = [
],
declarations: [
AlgoliaActionComponent,
AzureQueueActionComponent,
ContentChangedTriggerComponent,
RuleEventsPageComponent,
RulesPageComponent,

12
src/Squidex/app/features/rules/pages/rules/actions/algolia-action.component.html

@ -1,8 +1,8 @@
<form [formGroup]="actionForm" class="form-horizontal" (ngSubmit)="save()">
<div class="form-group row">
<label class="col col-2 col-form-label" for="appId">App ID</label>
<label class="col col-3 col-form-label" for="appId">App ID</label>
<div class="col col-10">
<div class="col col-9">
<sqx-control-errors for="text" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="appId" formControlName="appId" />
@ -14,9 +14,9 @@
</div>
<div class="form-group row">
<label class="col col-2 col-form-label" for="apiKey">Api Key</label>
<label class="col col-3 col-form-label" for="apiKey">Api Key</label>
<div class="col col-10">
<div class="col col-9">
<sqx-control-errors for="apiKey" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="apiKey" formControlName="apiKey" />
@ -28,9 +28,9 @@
</div>
<div class="form-group row">
<label class="col col-2 col-form-label" for="sharedSecret">Index Name</label>
<label class="col col-3 col-form-label" for="sharedSecret">Index Name</label>
<div class="col col-10">
<div class="col col-9">
<sqx-control-errors for="indexName" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="indexName" formControlName="indexName" />

29
src/Squidex/app/features/rules/pages/rules/actions/azure-queue-action.component.html

@ -0,0 +1,29 @@
<form [formGroup]="actionForm" class="form-horizontal" (ngSubmit)="save()">
<div class="form-group row">
<label class="col col-3 col-form-label" for="queue">Connection String</label>
<div class="col col-9">
<sqx-control-errors for="text" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="connectionString" formControlName="connectionString" />
<small class="form-text text-muted">
The connection string to the storage account.
</small>
</div>
</div>
<div class="form-group row">
<label class="col col-3 col-form-label" for="queue">Queue Name</label>
<div class="col col-9">
<sqx-control-errors for="queue" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="queue" formControlName="queue" />
<small class="form-text text-muted">
The name of the queue.
</small>
</div>
</div>
</form>

2
src/Squidex/app/features/rules/pages/rules/actions/azure-queue-action.component.scss

@ -0,0 +1,2 @@
@import '_vars';
@import '_mixins';

61
src/Squidex/app/features/rules/pages/rules/actions/azure-queue-action.component.ts

@ -0,0 +1,61 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { ValidatorsEx } from 'shared';
@Component({
selector: 'sqx-azure-queue-action',
styleUrls: ['./azure-queue-action.component.scss'],
templateUrl: './azure-queue-action.component.html'
})
export class AzureQueueActionComponent implements OnInit {
@Input()
public action: any;
@Output()
public actionChanged = new EventEmitter<object>();
public actionFormSubmitted = false;
public actionForm =
this.formBuilder.group({
connectionString: ['',
[
Validators.required
]],
queue: ['squidex',
[
Validators.required,
ValidatorsEx.pattern('[a-z][a-z0-9]{2,}(\-[a-z0-9]+)*', 'Name must be a valid azure queue name.')
]]
});
constructor(
private readonly formBuilder: FormBuilder
) {
}
public ngOnInit() {
this.action = Object.assign({}, { connectionString: '', queue: 'squidex' }, this.action || {});
this.actionFormSubmitted = false;
this.actionForm.reset();
this.actionForm.setValue(this.action);
}
public save() {
this.actionFormSubmitted = true;
if (this.actionForm.valid) {
const action = this.actionForm.value;
this.actionChanged.emit(action);
}
}
}

8
src/Squidex/app/features/rules/pages/rules/actions/slack-action.component.html

@ -1,8 +1,8 @@
<form [formGroup]="actionForm" class="form-horizontal" (ngSubmit)="save()">
<div class="form-group row">
<label class="col col-2 col-form-label" for="webhookUrl">Webhook Url</label>
<label class="col col-3 col-form-label" for="webhookUrl">Webhook Url</label>
<div class="col col-10">
<div class="col col-9">
<sqx-control-errors for="webhookUrl" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="webhookUrl" class="form-control" id="webhookUrl" formControlName="webhookUrl" />
@ -14,9 +14,9 @@
</div>
<div class="form-group row">
<label class="col col-2 col-form-label" for="text">Text</label>
<label class="col col-3 col-form-label" for="text">Text</label>
<div class="col col-10">
<div class="col col-9">
<sqx-control-errors for="text" [submitted]="actionFormSubmitted"></sqx-control-errors>
<textarea class="form-control" id="text" formControlName="text"></textarea>

8
src/Squidex/app/features/rules/pages/rules/actions/webhook-action.component.html

@ -1,8 +1,8 @@
<form [formGroup]="actionForm" class="form-horizontal" (ngSubmit)="save()">
<div class="form-group row">
<label class="col col-2 col-form-label" for="url">Url</label>
<label class="col col-3 col-form-label" for="url">Url</label>
<div class="col col-10">
<div class="col col-9">
<sqx-control-errors for="url" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="url" class="form-control" id="url" formControlName="url" />
@ -14,9 +14,9 @@
</div>
<div class="form-group row">
<label class="col col-2 col-form-label" for="sharedSecret">Secret</label>
<label class="col col-3 col-form-label" for="sharedSecret">Secret</label>
<div class="col col-10">
<div class="col col-9">
<sqx-control-errors for="sharedSecret" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="sharedSecret" formControlName="sharedSecret" />

6
src/Squidex/app/features/rules/pages/rules/rule-wizard.component.html

@ -68,6 +68,12 @@
(actionChanged)="selectAction($event)">
</sqx-algolia-action>
</div>
<div *ngSwitchCase="'AzureQueue'">
<sqx-azure-queue-action #actionControl
[action]="action"
(actionChanged)="selectAction($event)">
</sqx-azure-queue-action>
</div>
<div *ngSwitchCase="'Slack'">
<sqx-slack-action #actionControl
[action]="action"

1
src/Squidex/app/shared/services/rules.service.ts

@ -26,6 +26,7 @@ export const ruleTriggers: any = {
export const ruleActions: any = {
'Algolia': 'Populate Algolia Index',
'AzureQueue': 'Send to Azure Queue',
'Slack': 'Send to Slack',
'Webhook': 'Send Webhook'
};

5
src/Squidex/app/theme/_rules.scss

@ -6,6 +6,7 @@ $trigger-content-changed: #3389ff;
$action-webhook: #4bb958;
$action-algolia: #0d9bf9;
$action-slack: #5c3a58;
$action-azure: #55b3ff;
@mixin build-element($color) {
& {
@ -51,6 +52,10 @@ $action-slack: #5c3a58;
@include build-element($trigger-content-changed);
}
.rule-element-AzureQueue {
@include build-element($action-azure);
}
.rule-element-Algolia {
@include build-element($action-algolia);
}

16
src/Squidex/app/theme/icomoon/demo.html

@ -1299,6 +1299,22 @@
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
<div class="glyph fs3">
<div class="clearfix bshadow0 pbs">
<span class="icon-action-AzureQueue">
</span>
<span class="mls"> icon-action-AzureQueue</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e940" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe940;" class="unitRight size1of2 talign-right" />
</fieldset>
<div class="fs0 bshadow0 clearfix hidden-true">
<span class="unit pvs fgc1">liga: </span>
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
<div class="glyph fs3">
<div class="clearfix bshadow0 pbs">
<span class="icon-pause">

BIN
src/Squidex/app/theme/icomoon/fonts/icomoon.eot

Binary file not shown.

2
src/Squidex/app/theme/icomoon/fonts/icomoon.svg

@ -71,7 +71,7 @@
<glyph unicode="&#xe93d;" glyph-name="bug" horiz-adv-x="951" d="M932.571 402.286c0-20-16.571-36.571-36.571-36.571h-128c0-71.429-15.429-125.143-38.286-165.714l118.857-119.429c14.286-14.286 14.286-37.143 0-51.429-6.857-7.429-16.571-10.857-25.714-10.857s-18.857 3.429-25.714 10.857l-113.143 112.571s-74.857-68.571-172-68.571v512h-73.143v-512c-103.429 0-178.857 75.429-178.857 75.429l-104.571-118.286c-7.429-8-17.143-12-27.429-12-8.571 0-17.143 2.857-24.571 9.143-14.857 13.714-16 36.571-2.857 52l115.429 129.714c-20 39.429-33.143 90.286-33.143 156.571h-128c-20 0-36.571 16.571-36.571 36.571s16.571 36.571 36.571 36.571h128v168l-98.857 98.857c-14.286 14.286-14.286 37.143 0 51.429s37.143 14.286 51.429 0l98.857-98.857h482.286l98.857 98.857c14.286 14.286 37.143 14.286 51.429 0s14.286-37.143 0-51.429l-98.857-98.857v-168h128c20 0 36.571-16.571 36.571-36.571zM658.286 731.428h-365.714c0 101.143 81.714 182.857 182.857 182.857s182.857-81.714 182.857-182.857z" />
<glyph unicode="&#xe93e;" glyph-name="download" d="M640 853.334q78 0 149.167-30.5t122.5-81.833 81.833-122.5 30.5-149.167q0-85-35-160.667t-96.667-129.167-140-77.5l21 20.667q18 18.333 28 42.667 9.333 22.667 9.333 49.333 0 6.667-0.333 9.333 59.333 41.333 93.833 105.833t34.5 139.5q0 60.667-23.667 116t-63.667 95.333-95.333 63.667-116 23.667q-55.333 0-106.5-19.833t-90-53.833-65-81.333-33.833-101h-88.667q-70.667 0-120.667-50t-50-120.667q0-38.667 15.167-71.667t39.833-54.167 54.833-33 60.833-11.833h50q11.667-29.333 30-48l37.667-37.333h-117.667q-69.667 0-128.5 34.333t-93.167 93.167-34.333 128.5 34.333 128.5 93.167 93.167 128.5 34.333h22q26.333 74.333 79.333 132.167t126.833 90.833 155.833 33zM554.667 512q17.667 0 30.167-12.5t12.5-30.167v-281l55 55.333q12.333 12.333 30.333 12.333 18.333 0 30.5-12.167t12.167-30.5q0-18-12.333-30.333l-128-128q-12.333-12.333-30.333-12.333t-30.333 12.333l-128 128q-12.333 13-12.333 30.333 0 17.667 12.5 30.167t30.167 12.5q18 0 30.333-12.333l55-55.333v281q0 17.667 12.5 30.167t30.167 12.5z" />
<glyph unicode="&#xe93f;" glyph-name="document-unpublish" d="M358.4 848.457c-28.314 0-51.2-22.886-51.2-51.2v-153.6h51.2v153.6h307.2v-153.6c0-28.314 22.886-51.2 51.2-51.2h153.6v-512h-358.4v-51.2h358.4c28.314 0 51.2 22.886 51.2 51.2v548.2l-219.8 219.8h-292.2zM716.8 761.057l117.4-117.4h-117.4zM332.8 490.057l-230.4-256v-51.2h102.4v-153.6h256v153.6h102.4v51.2zM332.8 413.557l161.5-179.5h-84.7v-153.6h-153.6v153.6h-84.7zM102.4 593.325h460.8v-52.068h-460.8v52.068z" />
<glyph unicode="&#xe940;" glyph-name="microsoft" d="M0.35 448l-0.35 312.074 384 52.144v-364.218zM448 821.518l511.872 74.482v-448h-511.872zM959.998 384l-0.126-448-511.872 72.016v375.984zM384 16.164l-383.688 52.594-0.020 315.242h383.708z" />
<glyph unicode="&#xe940;" glyph-name="microsoft, action-AzureQueue" d="M0.35 448l-0.35 312.074 384 52.144v-364.218zM448 821.518l511.872 74.482v-448h-511.872zM959.998 384l-0.126-448-511.872 72.016v375.984zM384 16.164l-383.688 52.594-0.020 315.242h383.708z" />
<glyph unicode="&#xe941;" glyph-name="github" d="M512 960c-282.88 0-512-229.248-512-512 0-226.24 146.688-418.112 350.080-485.76 25.6-4.8 35.008 11.008 35.008 24.64 0 12.16-0.448 44.352-0.64 87.040-142.464-30.912-172.48 68.672-172.48 68.672-23.296 59.136-56.96 74.88-56.96 74.88-46.4 31.744 3.584 31.104 3.584 31.104 51.392-3.584 78.4-52.736 78.4-52.736 45.696-78.272 119.872-55.68 149.12-42.56 4.608 33.088 17.792 55.68 32.448 68.48-113.728 12.8-233.216 56.832-233.216 252.992 0 55.872 19.84 101.568 52.672 137.408-5.76 12.928-23.040 64.96 4.48 135.488 0 0 42.88 13.76 140.8-52.48 40.96 11.392 84.48 17.024 128 17.28 43.52-0.256 87.040-5.888 128-17.28 97.28 66.24 140.16 52.48 140.16 52.48 27.52-70.528 10.24-122.56 5.12-135.488 32.64-35.84 52.48-81.536 52.48-137.408 0-196.672-119.68-240-233.6-252.608 17.92-15.36 34.56-46.72 34.56-94.72 0-68.48-0.64-123.52-0.64-140.16 0-13.44 8.96-29.44 35.2-24.32 204.864 67.136 351.424 259.136 351.424 485.056 0 282.752-229.248 512-512 512z" />
<glyph unicode="&#xe942;" glyph-name="checkmark" d="M927.936 687.008l-68.288 68.288c-12.608 12.576-32.96 12.576-45.536 0l-409.44-409.44-194.752 196.16c-12.576 12.576-32.928 12.576-45.536 0l-68.288-68.288c-12.576-12.608-12.576-32.96 0-45.536l285.568-287.488c12.576-12.576 32.96-12.576 45.536 0l500.736 500.768c12.576 12.544 12.576 32.96 0 45.536z" />
<glyph unicode="&#xe943;" glyph-name="elapsed" d="M512.002 766.788v65.212h128v64c0 35.346-28.654 64-64.002 64h-191.998c-35.346 0-64-28.654-64-64v-64h128v-65.212c-214.798-16.338-384-195.802-384-414.788 0-229.75 186.25-416 416-416s416 186.25 416 416c0 218.984-169.202 398.448-384 414.788zM706.276 125.726c-60.442-60.44-140.798-93.726-226.274-93.726s-165.834 33.286-226.274 93.726c-60.44 60.44-93.726 140.8-93.726 226.274s33.286 165.834 93.726 226.274c58.040 58.038 134.448 91.018 216.114 93.548l-21.678-314.020c-1.86-26.29 12.464-37.802 31.836-37.802s33.698 11.512 31.836 37.802l-21.676 314.022c81.666-2.532 158.076-35.512 216.116-93.55 60.44-60.44 93.726-140.8 93.726-226.274s-33.286-165.834-93.726-226.274z" />

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

BIN
src/Squidex/app/theme/icomoon/fonts/icomoon.ttf

Binary file not shown.

BIN
src/Squidex/app/theme/icomoon/fonts/icomoon.woff

Binary file not shown.

2
src/Squidex/app/theme/icomoon/selection.json

@ -2121,7 +2121,7 @@
"id": 1,
"prevSize": 32,
"code": 59712,
"name": "microsoft"
"name": "microsoft, action-AzureQueue"
},
"setIdx": 2,
"setId": 1,

13
src/Squidex/app/theme/icomoon/style.css

@ -1,10 +1,10 @@
@font-face {
font-family: 'icomoon';
src: url('fonts/icomoon.eot?fb43dg');
src: url('fonts/icomoon.eot?fb43dg#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?fb43dg') format('truetype'),
url('fonts/icomoon.woff?fb43dg') format('woff'),
url('fonts/icomoon.svg?fb43dg#icomoon') format('svg');
src: url('fonts/icomoon.eot?knmn0p');
src: url('fonts/icomoon.eot?knmn0p#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?knmn0p') format('truetype'),
url('fonts/icomoon.woff?knmn0p') format('woff'),
url('fonts/icomoon.svg?knmn0p#icomoon') format('svg');
font-weight: normal;
font-style: normal;
}
@ -265,6 +265,9 @@
.icon-microsoft:before {
content: "\e940";
}
.icon-action-AzureQueue:before {
content: "\e940";
}
.icon-pause:before {
content: "\e92f";
}

31
tests/Squidex.Domain.Apps.Core.Tests/Model/Rules/RuleTests.cs

@ -6,6 +6,8 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@ -21,6 +23,21 @@ namespace Squidex.Domain.Apps.Core.Model.Rules
public class RuleTests
{
private readonly JsonSerializer serializer = TestData.DefaultSerializer();
public static readonly List<object[]> Actions =
typeof(Rule).Assembly.GetTypes()
.Where(x => x.BaseType == typeof(RuleAction))
.Select(Activator.CreateInstance)
.Select(x => new object[] { x })
.ToList();
public static readonly List<object[]> Triggers =
typeof(Rule).Assembly.GetTypes()
.Where(x => x.BaseType == typeof(RuleTrigger))
.Select(Activator.CreateInstance)
.Select(x => new object[] { x })
.ToList();
private readonly Rule rule_0 = new Rule(new ContentChangedTrigger(), new WebhookAction());
public sealed class OtherTrigger : RuleTrigger
@ -117,16 +134,18 @@ namespace Squidex.Domain.Apps.Core.Model.Rules
appClients.ShouldBeEquivalentTo(rule_0);
}
[Fact]
public void Should_freeze_webhook_action()
[Theory]
[MemberData(nameof(Actions))]
public void Should_freeze_actions(RuleAction action)
{
TestData.TestFreeze(new WebhookAction());
TestData.TestFreeze(action);
}
[Fact]
public void Should_freeze_contentchanged_trigger()
[Theory]
[MemberData(nameof(Triggers))]
public void Should_freeze_triggers(RuleTrigger trigger)
{
TestData.TestFreeze(new ContentChangedTrigger());
TestData.TestFreeze(trigger);
}
}
}

64
tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaFieldTests.cs

@ -6,6 +6,8 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using Squidex.Domain.Apps.Core.Schemas;
using Xunit;
@ -15,6 +17,13 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas
{
public class SchemaFieldTests
{
public static readonly List<object[]> FieldProperties =
typeof(Schema).Assembly.GetTypes()
.Where(x => x.BaseType == typeof(FieldProperties))
.Select(Activator.CreateInstance)
.Select(x => new object[] { x })
.ToList();
private readonly NumberField field_0 = new NumberField(1, "my-field", Partitioning.Invariant);
[Fact]
@ -97,58 +106,11 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas
Assert.Throws<ArgumentException>(() => field_0.Update(new StringFieldProperties()));
}
[Fact]
public void Should_freeze_asset_field_properties()
{
TestData.TestFreeze(new AssetsFieldProperties());
}
[Fact]
public void Should_freeze_boolean_field_properties()
{
TestData.TestFreeze(new BooleanFieldProperties());
}
[Fact]
public void Should_freeze_datetime_field_properties()
{
TestData.TestFreeze(new DateTimeFieldProperties());
}
[Fact]
public void Should_freeze_geolocation_field_properties()
{
TestData.TestFreeze(new GeolocationFieldProperties());
}
[Fact]
public void Should_freeze_json_field_properties()
{
TestData.TestFreeze(new JsonFieldProperties());
}
[Fact]
public void Should_freeze_number_field_properties()
{
TestData.TestFreeze(new NumberFieldProperties());
}
[Fact]
public void Should_freeze_references_field_properties()
{
TestData.TestFreeze(new ReferencesFieldProperties());
}
[Fact]
public void Should_freeze_string_field_properties()
{
TestData.TestFreeze(new StringFieldProperties());
}
[Fact]
public void Should_freeze_tags_field_properties()
[Theory]
[MemberData(nameof(FieldProperties))]
public void Should_freeze_field_properties(FieldProperties action)
{
TestData.TestFreeze(new TagsFieldProperties());
TestData.TestFreeze(action);
}
}
}

1
tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs

@ -26,7 +26,6 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas
[Fact]
public void Should_instantiate_schema()
{
Assert.True(schema_0.Properties.IsFrozen);
Assert.Equal("my-schema", schema_0.Name);
}

2
tests/Squidex.Domain.Apps.Core.Tests/TestData.cs

@ -101,7 +101,7 @@ namespace Squidex.Domain.Apps.Core
return schema;
}
public static void TestFreeze(Freezable freezable)
public static void TestFreeze(IFreezable freezable)
{
var sut = new AssetsFieldProperties();

56
tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Actions/AzureQueueActionTests.cs

@ -0,0 +1,56 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Rules.Actions;
using Xunit;
namespace Squidex.Domain.Apps.Entities.Rules.Guards.Actions
{
public sealed class AzureQueueActionTests
{
[Fact]
public async Task Should_add_error_if_connection_string_is_null()
{
var action = new AzureQueueAction { ConnectionString = null, Queue = "squidex" };
var errors = await RuleActionValidator.ValidateAsync(action);
Assert.NotEmpty(errors);
}
[Fact]
public async Task Should_add_error_if_queue_is_null()
{
var action = new AzureQueueAction { ConnectionString = "connection", Queue = null };
var errors = await RuleActionValidator.ValidateAsync(action);
Assert.NotEmpty(errors);
}
[Fact]
public async Task Should_add_error_if_queue_is_invalid()
{
var action = new AzureQueueAction { ConnectionString = "connection", Queue = "Squidex" };
var errors = await RuleActionValidator.ValidateAsync(action);
Assert.NotEmpty(errors);
}
[Fact]
public async Task Should_not_add_error_if_values_are_valid()
{
var action = new AzureQueueAction { ConnectionString = "connection", Queue = "squidex" };
var errors = await RuleActionValidator.ValidateAsync(action);
Assert.Empty(errors);
}
}
}
Loading…
Cancel
Save