mirror of https://github.com/Squidex/squidex.git
28 changed files with 681 additions and 167 deletions
@ -0,0 +1,66 @@ |
|||
// ==========================================================================
|
|||
// 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(AlgoliaAction))] |
|||
public sealed class AlgoliaAction : RuleAction |
|||
{ |
|||
private string appId; |
|||
private string apiKey; |
|||
private string indexName; |
|||
|
|||
public string AppId |
|||
{ |
|||
get |
|||
{ |
|||
return appId; |
|||
} |
|||
set |
|||
{ |
|||
ThrowIfFrozen(); |
|||
|
|||
appId = value; |
|||
} |
|||
} |
|||
|
|||
public string ApiKey |
|||
{ |
|||
get |
|||
{ |
|||
return apiKey; |
|||
} |
|||
set |
|||
{ |
|||
ThrowIfFrozen(); |
|||
|
|||
apiKey = value; |
|||
} |
|||
} |
|||
|
|||
public string IndexName |
|||
{ |
|||
get |
|||
{ |
|||
return indexName; |
|||
} |
|||
set |
|||
{ |
|||
ThrowIfFrozen(); |
|||
|
|||
indexName = value; |
|||
} |
|||
} |
|||
|
|||
public override T Accept<T>(IRuleActionVisitor<T> visitor) |
|||
{ |
|||
return visitor.Visit(this); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,141 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Threading.Tasks; |
|||
using Algolia.Search; |
|||
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.Domain.Apps.Events.Contents; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.EventSourcing; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.HandleRules.Actions |
|||
{ |
|||
public sealed class AlgoliaActionHandler : RuleActionHandler<AlgoliaAction> |
|||
{ |
|||
private readonly ConcurrentDictionary<(string AppId, string ApiKey, string IndexName), Index> clients = new ConcurrentDictionary<(string AppId, string ApiKey, string IndexName), Index>(); |
|||
private readonly JsonSerializer serializer; |
|||
|
|||
public AlgoliaActionHandler(JsonSerializer serializer) |
|||
{ |
|||
Guard.NotNull(serializer, nameof(serializer)); |
|||
|
|||
this.serializer = serializer; |
|||
} |
|||
|
|||
protected override (string Description, RuleJobData Data) CreateJob(Envelope<AppEvent> @event, string eventName, AlgoliaAction action) |
|||
{ |
|||
var ruleDescription = string.Empty; |
|||
var ruleData = new RuleJobData |
|||
{ |
|||
["AppId"] = action.AppId, |
|||
["ApiKey"] = action.ApiKey, |
|||
["IndexName"] = action.IndexName |
|||
}; |
|||
|
|||
if (@event.Payload is ContentEvent contentEvent) |
|||
{ |
|||
ruleData["ContentId"] = contentEvent.ContentId.ToString(); |
|||
ruleData["Operation"] = "Upsert"; |
|||
|
|||
switch (@event.Payload) |
|||
{ |
|||
case ContentCreated created: |
|||
{ |
|||
ruleDescription = $"Add entry to Algolia index: {action.IndexName}"; |
|||
ruleData["Content"] = new JObject( |
|||
new JProperty("data", JObject.FromObject(created.Data, serializer))); |
|||
break; |
|||
} |
|||
|
|||
case ContentUpdated updated: |
|||
{ |
|||
ruleDescription = $"Update entry in Algolia index: {action.IndexName}"; |
|||
ruleData["Content"] = new JObject( |
|||
new JProperty("data", JObject.FromObject(updated.Data, serializer))); |
|||
break; |
|||
} |
|||
|
|||
case ContentStatusChanged statusChanged: |
|||
{ |
|||
ruleDescription = $"Update entry in Algolia index: {action.IndexName}"; |
|||
ruleData["Content"] = new JObject( |
|||
new JProperty("status", statusChanged.Status.ToString())); |
|||
break; |
|||
} |
|||
|
|||
case ContentDeleted deleted: |
|||
{ |
|||
ruleDescription = $"Delete entry from Index: {action.IndexName}"; |
|||
ruleData["Content"] = new JObject(); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return (ruleDescription, ruleData); |
|||
} |
|||
|
|||
public override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(RuleJobData job) |
|||
{ |
|||
var appId = (string)job["AppId"]; |
|||
var apiKey = (string)job["ApiKey"]; |
|||
var indexName = (string)job["IndexName"]; |
|||
|
|||
var index = clients.GetOrAdd((appId, apiKey, indexName), s => |
|||
{ |
|||
var client = new AlgoliaClient(appId, apiKey); |
|||
|
|||
return client.InitIndex(indexName); |
|||
}); |
|||
|
|||
var operation = (string)job["Operation"]; |
|||
var content = (JObject)job["Content"]; |
|||
var contentId = (string)job["ContentId"]; |
|||
|
|||
try |
|||
{ |
|||
switch (operation) |
|||
{ |
|||
case "Upsert": |
|||
{ |
|||
content["objectID"] = contentId; |
|||
|
|||
var resonse = await index.PartialUpdateObjectAsync(content); |
|||
|
|||
return (resonse.ToString(Formatting.Indented), null); |
|||
} |
|||
|
|||
case "Delete": |
|||
{ |
|||
var resonse = await index.DeleteObjectAsync(contentId); |
|||
|
|||
return (resonse.ToString(Formatting.Indented), null); |
|||
} |
|||
|
|||
default: |
|||
{ |
|||
return ("Nothing to do!", null); |
|||
} |
|||
} |
|||
} |
|||
catch (AlgoliaException ex) |
|||
{ |
|||
return (ex.Message, ex); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
return (null, ex); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
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("Algolia")] |
|||
public sealed class AlgoliaActionDto : RuleActionDto |
|||
{ |
|||
/// <summary>
|
|||
/// The application ID.
|
|||
/// </summary>
|
|||
[Required] |
|||
public string AppId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The API key to grant access to Squidex.
|
|||
/// </summary>
|
|||
[Required] |
|||
public string ApiKey { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The name of the index.
|
|||
/// </summary>
|
|||
[Required] |
|||
public string IndexName { get; set; } |
|||
|
|||
public override RuleAction ToAction() |
|||
{ |
|||
return SimpleMapper.Map(this, new AlgoliaAction()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
<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> |
|||
|
|||
<div class="col col-10"> |
|||
<sqx-control-errors for="text" [submitted]="actionFormSubmitted"></sqx-control-errors> |
|||
|
|||
<input type="text" class="form-control" id="appId" formControlName="appId" /> |
|||
|
|||
<small class="form-text text-muted"> |
|||
The ID to you algolia application. |
|||
</small> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="form-group row"> |
|||
<label class="col col-2 col-form-label" for="apiKey">Api Key</label> |
|||
|
|||
<div class="col col-10"> |
|||
<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 to access you algolia app. |
|||
</small> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="form-group row"> |
|||
<label class="col col-2 col-form-label" for="sharedSecret">Index Name</label> |
|||
|
|||
<div class="col col-10"> |
|||
<sqx-control-errors for="indexName" [submitted]="actionFormSubmitted"></sqx-control-errors> |
|||
|
|||
<input type="text" class="form-control" id="indexName" formControlName="indexName" /> |
|||
|
|||
<small class="form-text text-muted"> |
|||
The name of the index. Use $SCHEMA_NAME as a placeholder |
|||
</small> |
|||
</div> |
|||
</div> |
|||
</form> |
|||
@ -0,0 +1,2 @@ |
|||
@import '_vars'; |
|||
@import '_mixins'; |
|||
@ -0,0 +1,62 @@ |
|||
/* |
|||
* 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-algolia-action', |
|||
styleUrls: ['./algolia-action.component.scss'], |
|||
templateUrl: './algolia-action.component.html' |
|||
}) |
|||
export class AlgoliaActionComponent implements OnInit { |
|||
@Input() |
|||
public action: any; |
|||
|
|||
@Output() |
|||
public actionChanged = new EventEmitter<object>(); |
|||
|
|||
public actionFormSubmitted = false; |
|||
public actionForm = |
|||
this.formBuilder.group({ |
|||
appId: ['', |
|||
[ |
|||
Validators.required |
|||
]], |
|||
apiKey: ['', |
|||
[ |
|||
Validators.required |
|||
]], |
|||
indexName: ['$SCHEMA_NAME', |
|||
[ |
|||
Validators.required |
|||
]] |
|||
}); |
|||
|
|||
constructor( |
|||
private readonly formBuilder: FormBuilder |
|||
) { |
|||
} |
|||
|
|||
public ngOnInit() { |
|||
this.action = Object.assign({}, { appId: '', apiKey: '', indexName: '$SCHEMA_NAME' }, 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); |
|||
} |
|||
} |
|||
} |
|||
Binary file not shown.
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
Loading…
Reference in new issue