mirror of https://github.com/Squidex/squidex.git
25 changed files with 347 additions and 11 deletions
@ -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(FastlyAction))] |
||||
|
public sealed class FastlyAction : RuleAction |
||||
|
{ |
||||
|
public string ApiKey { get; set; } |
||||
|
|
||||
|
public string ServiceId { get; set; } |
||||
|
|
||||
|
public override T Accept<T>(IRuleActionVisitor<T> visitor) |
||||
|
{ |
||||
|
return visitor.Visit(this); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,92 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Net.Http; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
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; |
||||
|
using Squidex.Infrastructure.Http; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Core.HandleRules.Actions |
||||
|
{ |
||||
|
public sealed class FastlyActionHandler : RuleActionHandler<FastlyAction> |
||||
|
{ |
||||
|
protected override (string Description, RuleJobData Data) CreateJob(Envelope<AppEvent> @event, string eventName, FastlyAction action) |
||||
|
{ |
||||
|
var ruleDescription = "Purge key in fastly"; |
||||
|
var ruleData = new RuleJobData |
||||
|
{ |
||||
|
["FastlyApiKey"] = action.ApiKey, |
||||
|
["FastlyServiceID"] = action.ServiceId |
||||
|
}; |
||||
|
|
||||
|
if (@event.Headers.Contains(CommonHeaders.AggregateId)) |
||||
|
{ |
||||
|
ruleData["Key"] = @event.Headers.AggregateId().ToString(); |
||||
|
} |
||||
|
|
||||
|
return (ruleDescription, ruleData); |
||||
|
} |
||||
|
|
||||
|
public override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(RuleJobData job) |
||||
|
{ |
||||
|
if (!job.TryGetValue("Key", out var keyToken)) |
||||
|
{ |
||||
|
return (null, new InvalidOperationException("The action cannot handle this event.")); |
||||
|
} |
||||
|
|
||||
|
var requestMsg = BuildRequest(job, keyToken.Value<string>()); |
||||
|
|
||||
|
HttpResponseMessage response = null; |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
response = await HttpClientPool.GetHttpClient().SendAsync(requestMsg); |
||||
|
|
||||
|
var responseString = await response.Content.ReadAsStringAsync(); |
||||
|
var requestDump = DumpFormatter.BuildDump(requestMsg, response, null, responseString, TimeSpan.Zero, false); |
||||
|
|
||||
|
return (requestDump, null); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
if (requestMsg != null) |
||||
|
{ |
||||
|
var requestDump = DumpFormatter.BuildDump(requestMsg, response, null, ex.ToString(), TimeSpan.Zero, false); |
||||
|
|
||||
|
return (requestDump, ex); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var requestDump = ex.ToString(); |
||||
|
|
||||
|
return (requestDump, ex); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static HttpRequestMessage BuildRequest(Dictionary<string, JToken> job, string key) |
||||
|
{ |
||||
|
var serviceId = job["FastlyServiceID"].Value<string>(); |
||||
|
|
||||
|
var requestUrl = $"https://api.fastly.com/service/{serviceId}/purge/{key}"; |
||||
|
var request = new HttpRequestMessage(HttpMethod.Post, requestUrl); |
||||
|
|
||||
|
request.Headers.Add("Fastly-Key", job["FastlyApiKey"].Value<string>()); |
||||
|
|
||||
|
return request; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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("Fastly")] |
||||
|
public sealed class FastlyActionDto : RuleActionDto |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The ID of the fastly service.
|
||||
|
/// </summary>
|
||||
|
[Required] |
||||
|
public string ServiceId { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The API key to grant access to Squidex.
|
||||
|
/// </summary>
|
||||
|
[Required] |
||||
|
public string ApiKey { get; set; } |
||||
|
|
||||
|
public override RuleAction ToAction() |
||||
|
{ |
||||
|
return SimpleMapper.Map(this, new FastlyAction()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
<h3 class="rule-title">Purge cache entries in Fastly</h3> |
||||
|
|
||||
|
<form [formGroup]="actionForm" class="form-horizontal" (ngSubmit)="save()"> |
||||
|
<div class="form-group row"> |
||||
|
<label class="col col-3 col-form-label" for="serviceId">Service ID</label> |
||||
|
|
||||
|
<div class="col col-9"> |
||||
|
<sqx-control-errors for="text" [submitted]="actionFormSubmitted"></sqx-control-errors> |
||||
|
|
||||
|
<input type="text" class="form-control" id="serviceId" formControlName="serviceId" /> |
||||
|
|
||||
|
<small class="form-text text-muted"> |
||||
|
The service ID of the fastly account. |
||||
|
</small> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-group row"> |
||||
|
<label class="col col-3 col-form-label" for="apiKey">Api Key</label> |
||||
|
|
||||
|
<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" /> |
||||
|
|
||||
|
<small class="form-text text-muted"> |
||||
|
The API key for the fastly account. |
||||
|
</small> |
||||
|
</div> |
||||
|
</div> |
||||
|
</form> |
||||
@ -0,0 +1,2 @@ |
|||||
|
@import '_vars'; |
||||
|
@import '_mixins'; |
||||
@ -0,0 +1,58 @@ |
|||||
|
/* |
||||
|
* 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'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'sqx-fastly-action', |
||||
|
styleUrls: ['./fastly-action.component.scss'], |
||||
|
templateUrl: './fastly-action.component.html' |
||||
|
}) |
||||
|
export class FastlyActionComponent implements OnInit { |
||||
|
@Input() |
||||
|
public action: any; |
||||
|
|
||||
|
@Output() |
||||
|
public actionChanged = new EventEmitter<object>(); |
||||
|
|
||||
|
public actionFormSubmitted = false; |
||||
|
public actionForm = |
||||
|
this.formBuilder.group({ |
||||
|
serviceId: ['', |
||||
|
[ |
||||
|
Validators.required |
||||
|
]], |
||||
|
apiKey: ['', |
||||
|
[ |
||||
|
Validators.required |
||||
|
]] |
||||
|
}); |
||||
|
|
||||
|
constructor( |
||||
|
private readonly formBuilder: FormBuilder |
||||
|
) { |
||||
|
} |
||||
|
|
||||
|
public ngOnInit() { |
||||
|
this.action = Object.assign({}, { serviceId: '', apiKey: '' }, 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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
// ==========================================================================
|
||||
|
// 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 FastlyActionTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public async Task Should_add_error_if_service_id_not_defined() |
||||
|
{ |
||||
|
var action = new FastlyAction { ServiceId = null, ApiKey = "KEY" }; |
||||
|
|
||||
|
var errors = await RuleActionValidator.ValidateAsync(action); |
||||
|
|
||||
|
Assert.NotEmpty(errors); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_error_if_api_key_not_defined() |
||||
|
{ |
||||
|
var action = new FastlyAction { ServiceId = "APP", ApiKey = null }; |
||||
|
|
||||
|
var errors = await RuleActionValidator.ValidateAsync(action); |
||||
|
|
||||
|
Assert.NotEmpty(errors); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_add_error_everything_defined() |
||||
|
{ |
||||
|
var action = new FastlyAction { ServiceId = "APP", ApiKey = "KEY" }; |
||||
|
|
||||
|
var errors = await RuleActionValidator.ValidateAsync(action); |
||||
|
|
||||
|
Assert.Empty(errors); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue