Merge pull request #6438 from elsa-workflows/task/remove-orchard-core

Remove Elsa.OrchardCore module.
This commit is contained in:
Sipke Schoorstra 2025-02-23 20:10:51 +01:00 committed by GitHub
commit a8e5a97f93
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 0 additions and 781 deletions

View file

@ -302,8 +302,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "trace-lens", "trace-lens",
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "integrations", "integrations", "{EBD0CF78-C5D7-4B4B-94C9-C5D8C20B6F59}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.OrchardCore", "src\modules\Elsa.OrchardCore\Elsa.OrchardCore.csproj", "{B0460856-7E6D-4B1E-B243-8E20662FC32E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "agents", "agents", "{50470834-4CD8-479A-8B58-0A1869BA5D37}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Agents.Core", "src\modules\Elsa.Agents.Core\Elsa.Agents.Core.csproj", "{5BD671F0-9603-4C2F-8364-54B02986EAA2}"
@ -755,10 +753,6 @@ Global
{47FBCB04-0C2D-453C-BE2F-7052CAC22524}.Debug|Any CPU.Build.0 = Debug|Any CPU
{47FBCB04-0C2D-453C-BE2F-7052CAC22524}.Release|Any CPU.ActiveCfg = Release|Any CPU
{47FBCB04-0C2D-453C-BE2F-7052CAC22524}.Release|Any CPU.Build.0 = Release|Any CPU
{B0460856-7E6D-4B1E-B243-8E20662FC32E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B0460856-7E6D-4B1E-B243-8E20662FC32E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B0460856-7E6D-4B1E-B243-8E20662FC32E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B0460856-7E6D-4B1E-B243-8E20662FC32E}.Release|Any CPU.Build.0 = Release|Any CPU
{5BD671F0-9603-4C2F-8364-54B02986EAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5BD671F0-9603-4C2F-8364-54B02986EAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5BD671F0-9603-4C2F-8364-54B02986EAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -1005,7 +999,6 @@ Global
{6DDB9ECF-D454-4894-A262-A6BB3026E61E} = {B32DB9B2-AD6C-48A5-8682-4373CB045185}
{16B570BC-E293-4A19-B20E-5C97BAF8476B} = {B32DB9B2-AD6C-48A5-8682-4373CB045185}
{EBD0CF78-C5D7-4B4B-94C9-C5D8C20B6F59} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79}
{B0460856-7E6D-4B1E-B243-8E20662FC32E} = {EBD0CF78-C5D7-4B4B-94C9-C5D8C20B6F59}
{50470834-4CD8-479A-8B58-0A1869BA5D37} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79}
{5BD671F0-9603-4C2F-8364-54B02986EAA2} = {50470834-4CD8-479A-8B58-0A1869BA5D37}
{154525CD-3726-45B7-9A9C-553BBC60EB10} = {50470834-4CD8-479A-8B58-0A1869BA5D37}

View file

@ -1,46 +0,0 @@
using Elsa.Expressions.Models;
using Elsa.Extensions;
using Elsa.Mediator.Contracts;
using Elsa.OrchardCore.Stimuli;
using Elsa.OrchardCore.WebhookPayloads;
using Elsa.Workflows;
using Elsa.Workflows.Attributes;
namespace Elsa.OrchardCore.Activities;
public class ContentItemEvent : Trigger<ContentItemEventPayload>, INotification
{
/// <summary>
/// The content type to handle the event for.
/// </summary>
[Input(Description = "The content type to handle the event for.")]
public string ContentType { get; set; } = default!;
/// <summary>
/// The event to handle.
/// </summary>
[Input(Description = "The event to handle the event for.")]
public string EventType { get; set; } = default!;
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
if (!context.IsTriggerOfWorkflow())
{
context.CreateBookmark(GetStimulus(context.ExpressionExecutionContext), OnResumeAsync);
return;
}
await ExecuteInternalAsync(context);
}
protected override object GetTriggerPayload(TriggerIndexingContext context) => new ContentItemEventStimulus(ContentType, EventType);
private async ValueTask OnResumeAsync(ActivityExecutionContext context) => await ExecuteInternalAsync(context);
private object GetStimulus(ExpressionExecutionContext context) => new ContentItemEventStimulus(ContentType, EventType);
private async Task ExecuteInternalAsync(ActivityExecutionContext context)
{
var payload = context.GetWorkflowInput<ContentItemEventPayload>();
context.SetResult(payload);
await context.CompleteActivityAsync();
}
}

View file

@ -1,38 +0,0 @@
using System.Text.Json.Nodes;
using Elsa.Expressions.Helpers;
using Elsa.Extensions;
using Elsa.OrchardCore.Client;
using Elsa.Workflows;
using Elsa.Workflows.Attributes;
using Elsa.Workflows.Models;
namespace Elsa.OrchardCore.Activities;
[Activity("OrchardCore", "Orchard Core", "Patches a content item", Kind = ActivityKind.Task)]
public class CreateContentItem : CodeActivity<JsonObject>
{
[Input(Description = "The content type of the content item to create.")]
public Input<string> ContentType { get; set; } = default!;
[Input(Description = "The properties to apply to the content item.")]
public Input<object> Properties { get; set; } = default!;
[Input(Description = "Whether to publish the content item.")]
public Input<bool> Publish { get; set; } = default!;
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
var contentType = ContentType.Get(context);
var publish = Publish.Get(context);
var properties = Properties.Get(context).ConvertTo<JsonObject>()!;
var apiClient = context.GetRequiredService<IRestApiClient>();
var request = new CreateContentItemRequest
{
ContentType = contentType,
Properties = properties,
Publish = publish
};
var contentItem = await apiClient.CreateContentItemAsync(request, context.CancellationToken);
context.SetResult(contentItem);
}
}

View file

@ -1,31 +0,0 @@
using System.Text.Json.Nodes;
using Elsa.Extensions;
using Elsa.OrchardCore.Client;
using Elsa.Workflows;
using Elsa.Workflows.Attributes;
using Elsa.Workflows.Models;
namespace Elsa.OrchardCore.Activities;
[Activity("OrchardCore", "Orchard Core", "Localizes a content item", Kind = ActivityKind.Task)]
public class LocalizeContentItem : CodeActivity<JsonObject>
{
[Input(Description = "The ID of the content item to localize.")]
public Input<string> ContentItemId { get; set; } = default!;
[Input(Description = "The culture code to use when creating a localized version.")]
public Input<string> CultureCode { get; set; } = default!;
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
var contentItemId = ContentItemId.Get(context);
var cultureCode = CultureCode.Get(context);
var apiClient = context.GetRequiredService<IRestApiClient>();
var request = new LocalizeContentItemRequest
{
CultureCode = cultureCode
};
var localizedContentItem = await apiClient.LocalizeContentItemAsync(contentItemId, request, context.CancellationToken);
context.SetResult(localizedContentItem);
}
}

View file

@ -1,36 +0,0 @@
using System.Text.Json.Nodes;
using Elsa.Extensions;
using Elsa.OrchardCore.Client;
using Elsa.Workflows;
using Elsa.Workflows.Attributes;
using Elsa.Workflows.Models;
namespace Elsa.OrchardCore.Activities;
[Activity("OrchardCore", "Orchard Core", "Patches a content item", Kind = ActivityKind.Task)]
public class PatchContentItem : CodeActivity<JsonObject>
{
[Input(Description = "The ID of the content item to patch.")]
public Input<string> ContentItemId { get; set; } = default!;
[Input(Description = "The patch to apply to the content item.")]
public Input<JsonObject> Patch { get; set; } = default!;
[Input(Description = "Whether to publish the patched content item.")]
public Input<bool> Publish { get; set; } = default!;
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
var contentItemId = ContentItemId.Get(context);
var publish = Publish.Get(context);
var patch = Patch.Get(context);
var apiClient = context.GetRequiredService<IRestApiClient>();
var request = new PatchContentItemRequest
{
Patch = patch,
Publish = publish
};
var patchedContentItem = await apiClient.PatchContentItemAsync(contentItemId, request, context.CancellationToken);
context.SetResult(patchedContentItem);
}
}

View file

@ -1,27 +0,0 @@
using System.Text.Json.Nodes;
using Elsa.Extensions;
using Elsa.OrchardCore.Client;
using Elsa.Workflows;
using Elsa.Workflows.Attributes;
using Elsa.Workflows.Models;
namespace Elsa.OrchardCore.Activities;
[Activity("OrchardCore", "Orchard Core", "Returns tag content items for the specified set of tags. If a tag doesn't exist, it is created.", Kind = ActivityKind.Task)]
public class ResolveTags : CodeActivity<JsonObject>
{
[Input(Description = "The tags to resolve.")]
public Input<ICollection<string>> Tags { get; set; } = default!;
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
var tags = Tags.Get(context);
var apiClient = context.GetRequiredService<IRestApiClient>();
var request = new ResolveTagsRequest
{
Tags = tags
};
var result = await apiClient.ResolveTagsAsync(request, context.CancellationToken);
context.SetResult(result);
}
}

View file

@ -1,26 +0,0 @@
using Elsa.Extensions;
using Elsa.OrchardCore.Client;
using Elsa.Workflows;
using Elsa.Workflows.Attributes;
using Elsa.Workflows.Models;
namespace Elsa.OrchardCore.Activities;
[Activity("OrchardCore", "Orchard Core", "Send a GraphQL query to Orchard Core", DisplayName = "GraphQL Query", Kind = ActivityKind.Task)]
public class SendGraphQLQuery : CodeActivity<object>
{
/// <summary>
/// The content type to handle the event for.
/// </summary>
[Input(Description = "The GraphQL query string to send.")]
public Input<string> Query { get; set; } = default!;
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
var query = Query.Get(context);
var client = context.GetRequiredService<IGraphQLClient>();
var targetType = Result.GetTargetType(context);
var output = await client.SendQueryAsync(query, targetType, context.CancellationToken);
context.SetResult(output);
}
}

View file

@ -1,28 +0,0 @@
using System.Text.Json.Nodes;
using Elsa.Extensions;
using Elsa.Http;
using Elsa.OrchardCore.Client;
using Elsa.Workflows;
using Elsa.Workflows.Attributes;
using Elsa.Workflows.Models;
namespace Elsa.OrchardCore.Activities;
[Activity("OrchardCore", "Orchard Core", "Patches a content item", Kind = ActivityKind.Task)]
public class UploadMedia : CodeActivity<JsonObject>
{
[Input(Description = "The files to upload into the Media Library.")]
public Input<ICollection<HttpFile>> Files { get; set; } = default!;
[Input(Description = "The path to the media library folder to upload.")]
public Input<string?> FolderPath { get; set; } = default!;
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
var files = Files.Get(context);
var folderPath = FolderPath.GetOrDefault(context);
var apiClient = context.GetRequiredService<IRestApiClient>();
var response = await apiClient.UploadFilesAsync(files, folderPath, context.CancellationToken);
context.SetResult(response);
}
}

View file

@ -1,62 +0,0 @@
using Elsa.OrchardCore.Activities;
using Elsa.OrchardCore.Models;
using Elsa.Workflows;
using Elsa.Workflows.Models;
using Humanizer;
using JetBrains.Annotations;
using Microsoft.Extensions.Options;
namespace Elsa.OrchardCore.ActivityProviders;
/// <summary>
/// An activity provider that generates activity types based on Orchard content type events.
/// </summary>
[UsedImplicitly]
public class OrchardContentItemsEventActivityProvider(IOptions<OrchardCoreOptions> options, IActivityFactory activityFactory, IActivityDescriber activityDescriber) : IActivityProvider
{
public async ValueTask<IEnumerable<ActivityDescriptor>> GetDescriptorsAsync(CancellationToken cancellationToken = default)
{
var contentTypes = options.Value.ContentTypes;
var matrix =
from contentType in contentTypes
from eventType in WebhookEventTypes.GetWebhookEventDescriptors()
select new
{
contentType,
eventType
};
var activities = (await Task.WhenAll(matrix.Select(async x => await CreateActivityDescriptorAsync(x.contentType, x.eventType, cancellationToken)))).ToList();
return activities;
}
private async Task<ActivityDescriptor> CreateActivityDescriptorAsync(string contentType, WebhookEventDescriptor eventDescriptor, CancellationToken cancellationToken = default)
{
var eventType = eventDescriptor.EventType;
var humanizedEventName = eventType.Humanize();
var description = $"Handles {humanizedEventName} events for {contentType} content items";
var name = $"{contentType}{eventType}";
var displayName = $"{contentType} {eventType}";
var fullTypeName = OrchardCoreActivityNameHelper.GetContentItemEventActivityFullTypeName(contentType, eventType);
var activityType = typeof(ContentItemEvent);
var activityDescriptor = await activityDescriber.DescribeActivityAsync(activityType, cancellationToken);
activityDescriptor.TypeName = fullTypeName;
activityDescriptor.Name = name;
activityDescriptor.DisplayName = displayName;
activityDescriptor.Category = "Orchard Core";
activityDescriptor.Description = description;
activityDescriptor.Constructor = context =>
{
var activity = (ContentItemEvent)activityFactory.Create(activityType, context);
activity.Type = fullTypeName;
activity.ContentType = contentType;
activity.EventType = eventType;
return activity;
};
foreach (var inputDescriptor in activityDescriptor.Inputs)
inputDescriptor.IsBrowsable = false;
return activityDescriptor;
}
}

View file

@ -1,6 +0,0 @@
namespace Elsa.OrchardCore.Client;
public interface IGraphQLClient
{
Task<object> SendQueryAsync(string query, Type? targetType, CancellationToken cancellationToken = default);
}

View file

@ -1,14 +0,0 @@
using System.Text.Json.Nodes;
using Elsa.Http;
namespace Elsa.OrchardCore.Client;
public interface IRestApiClient
{
Task<JsonObject> GetContentItemAsync(string contentItemId, CancellationToken cancellationToken = default);
Task<JsonObject> PatchContentItemAsync(string contentItemId, PatchContentItemRequest request, CancellationToken cancellationToken = default);
Task<JsonObject> LocalizeContentItemAsync(string contentItemId, LocalizeContentItemRequest request, CancellationToken cancellationToken = default);
Task<JsonObject> CreateContentItemAsync(CreateContentItemRequest request, CancellationToken cancellationToken = default);
Task<JsonObject> UploadFilesAsync(IEnumerable<HttpFile> files, string? folderPath = null, CancellationToken cancellationToken = default);
Task<JsonObject> ResolveTagsAsync(ResolveTagsRequest request, CancellationToken cancellationToken = default);
}

View file

@ -1,6 +0,0 @@
namespace Elsa.OrchardCore.Client;
public interface ISecurityTokenClient
{
Task<SecurityToken> GetSecurityTokenAsync(string clientId, string clientSecret, CancellationToken cancellationToken = default);
}

View file

@ -1,7 +0,0 @@
namespace Elsa.OrchardCore.Client;
public interface ISecurityTokenService
{
Task<SecurityToken> GetTokenAsync(CancellationToken cancellationToken = default);
Task<SecurityToken> RefreshTokenAsync(CancellationToken cancellationToken = default);
}

View file

@ -1,27 +0,0 @@
using System.Net;
using System.Net.Http.Headers;
namespace Elsa.OrchardCore.Client;
public class AuthenticatingDelegatingHandler(ISecurityTokenService tokenService) : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var token = await tokenService.GetTokenAsync(cancellationToken);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken);
var response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
// Token might be expired; refresh it
token = await tokenService.RefreshTokenAsync(cancellationToken);
// Retry the request with the new token
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken);
response = await base.SendAsync(request, cancellationToken);
}
return response;
}
}

View file

@ -1,35 +0,0 @@
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Elsa.OrchardCore.Client.Extensions;
[PublicAPI]
public static class OrchardCoreClientServiceCollectionExtensions
{
public static IServiceCollection AddOrchardCoreClient(this IServiceCollection services)
{
services.AddTransient<ISecurityTokenService, DefaultSecurityTokenService>();
services.AddTransient<AuthenticatingDelegatingHandler>();
services.AddHttpClient<ISecurityTokenClient, DefaultSecurityTokenClient>((sp, httpClient) =>
{
var options = sp.GetRequiredService<IOptions<OrchardCoreClientOptions>>().Value;
httpClient.BaseAddress = options.BaseAddress;
});
services.AddHttpClient<IGraphQLClient, DefaultGraphQlClient>((sp, httpClient) =>
{
var options = sp.GetRequiredService<IOptions<OrchardCoreClientOptions>>().Value;
httpClient.BaseAddress = options.BaseAddress;
}).AddHttpMessageHandler<AuthenticatingDelegatingHandler>();
services.AddHttpClient<IRestApiClient, DefaultRestApiClient>((sp, httpClient) =>
{
var options = sp.GetRequiredService<IOptions<OrchardCoreClientOptions>>().Value;
httpClient.BaseAddress = options.BaseAddress;
}).AddHttpMessageHandler<AuthenticatingDelegatingHandler>();
return services;
}
}

View file

@ -1,10 +0,0 @@
using System.Text.Json.Nodes;
namespace Elsa.OrchardCore.Client;
public class CreateContentItemRequest
{
public string ContentType { get; set; } = default!;
public JsonNode Properties { get; set; } = default!;
public bool Publish { get; set; }
}

View file

@ -1,6 +0,0 @@
namespace Elsa.OrchardCore.Client;
public class LocalizeContentItemRequest
{
public string CultureCode { get; set; } = default!;
}

View file

@ -1,9 +0,0 @@
using System.Text.Json.Nodes;
namespace Elsa.OrchardCore.Client;
public class PatchContentItemRequest
{
public JsonNode Patch { get; set; } = default!;
public bool Publish { get; set; }
}

View file

@ -1,6 +0,0 @@
namespace Elsa.OrchardCore.Client;
public class ResolveTagsRequest
{
public ICollection<string> Tags { get; set; } = [];
}

View file

@ -1,9 +0,0 @@
namespace Elsa.OrchardCore.Client;
/// <summary>
///
/// </summary>
/// <param name="AccessToken"></param>
/// <param name="TokenType"></param>
/// <param name="ExpiresIn"></param>
public record SecurityToken(string AccessToken, string TokenType, int ExpiresIn);

View file

@ -1,22 +0,0 @@
namespace Elsa.OrchardCore.Client;
/// <summary>
/// Options related to connecting to an Orchard Core tenant.
/// </summary>
public class OrchardCoreClientOptions
{
/// <summary>
/// The base address of the Orchard Core tenant to interact with.
/// </summary>
public Uri BaseAddress { get; set; } = default!;
/// <summary>
/// The client ID.
/// </summary>
public string ClientId { get; set; } = default!;
/// <summary>
/// The client secret.
/// </summary>
public string ClientSecret { get; set; } = default!;
}

View file

@ -1,18 +0,0 @@
using System.Text.Json.Nodes;
using Elsa.Expressions.Helpers;
namespace Elsa.OrchardCore.Client;
public class DefaultGraphQlClient(HttpClient httpClient) : IGraphQLClient
{
public async Task<object> SendQueryAsync(string query, Type? targetType = null, CancellationToken cancellationToken = default)
{
var content = new StringContent(query, System.Text.Encoding.UTF8, "application/graphql");
var response = await httpClient.PostAsync("api/graphql", content, cancellationToken);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync(cancellationToken);
targetType ??= typeof(JsonObject);
return ObjectConverter.ConvertTo(json, targetType);
}
}

View file

@ -1,64 +0,0 @@
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Nodes;
using Elsa.Http;
namespace Elsa.OrchardCore.Client;
public class DefaultRestApiClient(HttpClient httpClient) : IRestApiClient
{
public async Task<JsonObject> GetContentItemAsync(string contentItemId, CancellationToken cancellationToken = default)
{
var response = await httpClient.GetAsync($"api/content/{contentItemId}", cancellationToken);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<JsonObject>(cancellationToken: cancellationToken);
}
public async Task<JsonObject> PatchContentItemAsync(string contentItemId, PatchContentItemRequest request, CancellationToken cancellationToken = default)
{
var content = JsonContent.Create(request);
var response = await httpClient.PatchAsync($"api/content-items/{contentItemId}", content, cancellationToken);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<JsonObject>(cancellationToken: cancellationToken);
}
public async Task<JsonObject> LocalizeContentItemAsync(string contentItemId, LocalizeContentItemRequest request, CancellationToken cancellationToken = default)
{
var content = JsonContent.Create(request);
var response = await httpClient.PostAsync($"api/content-items/{contentItemId}/localize", content, cancellationToken);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync(cancellationToken);
return JsonSerializer.Deserialize<JsonObject>(json);
}
public async Task<JsonObject> CreateContentItemAsync(CreateContentItemRequest request, CancellationToken cancellationToken = default)
{
var content = JsonContent.Create(request);
var response = await httpClient.PostAsync("api/content-items", content, cancellationToken);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<JsonObject>(cancellationToken: cancellationToken);
}
public async Task<JsonObject> UploadFilesAsync(IEnumerable<HttpFile> files, string? folderPath = null, CancellationToken cancellationToken = default)
{
var content = new MultipartFormDataContent();
foreach (var file in files)
{
var streamContent = file.GetStreamContent();
content.Add(streamContent);
}
var response = await httpClient.PostAsync($"api/media/upload?path={folderPath}", content, cancellationToken);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<JsonObject>(cancellationToken: cancellationToken);
}
public async Task<JsonObject> ResolveTagsAsync(ResolveTagsRequest request, CancellationToken cancellationToken = default)
{
var content = JsonContent.Create(request);
var response = await httpClient.PostAsync("api/tags/resolve", content, cancellationToken);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<JsonObject>(cancellationToken: cancellationToken);
}
}

View file

@ -1,26 +0,0 @@
using System.Text.Json;
namespace Elsa.OrchardCore.Client;
public class DefaultSecurityTokenClient(HttpClient httpClient) : ISecurityTokenClient
{
private readonly JsonSerializerOptions _jsonSerializerOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
};
public async Task<SecurityToken> GetSecurityTokenAsync(string clientId, string clientSecret, CancellationToken cancellationToken = default)
{
var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "client_credentials",
["client_id"] = clientId,
["client_secret"] = clientSecret
});
var response = await httpClient.PostAsync("/connect/token", content, cancellationToken);
response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
return JsonSerializer.Deserialize<SecurityToken>(responseContent, _jsonSerializerOptions);
}
}

View file

@ -1,31 +0,0 @@
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
namespace Elsa.OrchardCore.Client;
public class DefaultSecurityTokenService(IMemoryCache memoryCache, ISecurityTokenClient securityTokenClient, IOptions<OrchardCoreClientOptions> options) : ISecurityTokenService
{
private readonly string _cacheKey = $"{nameof(DefaultSecurityTokenService)}:{Guid.NewGuid()}";
public async Task<SecurityToken> GetTokenAsync(CancellationToken cancellationToken = default)
{
return (await memoryCache.GetOrCreateAsync(_cacheKey, async entry =>
{
var securityToken = await RequestSecurityTokenAsync(cancellationToken);
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(securityToken.ExpiresIn);
return securityToken;
}))!;
}
public Task<SecurityToken> RefreshTokenAsync(CancellationToken cancellationToken = default)
{
return RequestSecurityTokenAsync(cancellationToken);
}
private Task<SecurityToken> RequestSecurityTokenAsync(CancellationToken cancellationToken)
{
var clientId = options.Value.ClientId;
var clientSecret = options.Value.ClientSecret;
return securityTokenClient.GetSecurityTokenAsync(clientId, clientSecret, cancellationToken);
}
}

View file

@ -1,18 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>
Provides integrations with an Orchard Core application instances through activities and via HTTP.
</Description>
<PackageTags>elsa module orchardcore</PackageTags>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Elsa.Http\Elsa.Http.csproj" />
<ProjectReference Include="..\Elsa.Webhooks\Elsa.Webhooks.csproj" />
<ProjectReference Include="..\Elsa.Workflows.Core\Elsa.Workflows.Core.csproj" />
<ProjectReference Include="..\Elsa.Workflows.Management\Elsa.Workflows.Management.csproj" />
<ProjectReference Include="..\Elsa.Workflows.Runtime\Elsa.Workflows.Runtime.csproj" />
</ItemGroup>
</Project>

View file

@ -1,9 +0,0 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=client_005Ccontracts/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=client_005Cdelegatinghandlers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=client_005Cmodels/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=client_005Coptions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=client_005Cservices/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=constants/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=helpers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=options/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View file

@ -1,20 +0,0 @@
using Elsa.Features.Services;
using Elsa.OrchardCore.Features;
// ReSharper disable once CheckNamespace
namespace Elsa.Extensions;
/// <summary>
/// Adds extensions to <see cref="IModule"/> that enables the <see cref="OrchardCoreFeature"/> feature.
/// </summary>
public static class ModuleExtensions
{
/// <summary>
/// Enables and configures the <see cref="OrchardCoreFeature"/> feature.
/// </summary>
public static IModule UseOrchardCore(this IModule module, Action<OrchardCoreFeature>? configure = default)
{
module.Configure(configure);
return module;
}
}

View file

@ -1,31 +0,0 @@
using Elsa.Extensions;
using Elsa.Features.Abstractions;
using Elsa.Features.Attributes;
using Elsa.Features.Services;
using Elsa.OrchardCore.ActivityProviders;
using Elsa.OrchardCore.Client.Extensions;
using Elsa.OrchardCore.WebhookPayloads;
using Elsa.Webhooks.Features;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.OrchardCore.Features;
[DependsOn(typeof(WebhooksFeature))]
public class OrchardCoreFeature(IModule module) : FeatureBase(module)
{
public override void Configure()
{
Module.AddVariableTypeAndAlias<ContentItemEventPayload>("ContentItemPublished", "Orchard");
Module.AddActivitiesFrom<OrchardCoreFeature>();
Services
.AddActivityProvider<OrchardContentItemsEventActivityProvider>()
.AddHandlersFrom<OrchardCoreFeature>()
;
}
public override void Apply()
{
Services.AddOrchardCoreClient();
Services.AddOptions<OrchardCoreOptions>();
}
}

View file

@ -1,3 +0,0 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait />
</Weavers>

View file

@ -1,42 +0,0 @@
using Elsa.Expressions.Helpers;
using Elsa.Mediator.Contracts;
using Elsa.OrchardCore.Stimuli;
using Elsa.OrchardCore.WebhookPayloads;
using Elsa.Webhooks.Notifications;
using Elsa.Workflows.Runtime;
using JetBrains.Annotations;
using WebhooksCore;
namespace Elsa.OrchardCore.Handlers;
/// <summary>
/// Invokes Orchard activities based on the received webhook.
/// </summary>
[UsedImplicitly]
public class InvokeOrchardWebhookEventActivities(IStimulusSender stimulusSender) : INotificationHandler<WebhookEventReceived>
{
public async Task HandleAsync(WebhookEventReceived notification, CancellationToken cancellationToken)
{
var webhookEvent = notification.WebhookEvent;
var webhookEventType = webhookEvent.EventType;
var webhookEventDescriptors = WebhookEventTypes.GetWebhookEventDescriptors().ToDictionary(x => x.WebhookEventType, x => x);
if(!webhookEventDescriptors.TryGetValue(webhookEventType, out var webhookEventDescriptor))
return;
var eventType = webhookEventDescriptor.EventType;
var contentItemEventPayload = (ContentItemEventPayload)webhookEvent.Payload.ConvertTo(webhookEventDescriptor.PayloadType)!;
var contentType = contentItemEventPayload.ContentType;
var fullTypeName = OrchardCoreActivityNameHelper.GetContentItemEventActivityFullTypeName(contentType, eventType);
var stimulus = new ContentItemEventStimulus(contentType, eventType);
var metadata = new StimulusMetadata
{
Input = new Dictionary<string, object>
{
[nameof(WebhookEvent)] = webhookEvent,
[nameof(ContentItemEventPayload)] = contentItemEventPayload
}
};
await stimulusSender.SendAsync(fullTypeName, stimulus, metadata, cancellationToken);
}
}

View file

@ -1,9 +0,0 @@
namespace Elsa.OrchardCore;
public static class OrchardCoreActivityNameHelper
{
public static string GetContentItemEventActivityFullTypeName(string contentType, string eventName)
{
return $"OrchardCore.ContentItem.{contentType}.{eventName}";
}
}

View file

@ -1,8 +0,0 @@
namespace Elsa.OrchardCore.Models;
public class WebhookEventDescriptor
{
public string WebhookEventType { get; set; }
public string EventType { get; set; }
public Type PayloadType { get; set; }
}

View file

@ -1,6 +0,0 @@
namespace Elsa.OrchardCore;
public class OrchardCoreOptions
{
public ISet<string> ContentTypes { get; set; } = new HashSet<string>();
}

View file

@ -1,3 +0,0 @@
namespace Elsa.OrchardCore.Stimuli;
public record ContentItemEventStimulus(string ContentType, string EventType);

View file

@ -1,27 +0,0 @@
using Elsa.OrchardCore.Models;
using Elsa.OrchardCore.WebhookPayloads;
using JetBrains.Annotations;
namespace Elsa.OrchardCore;
[UsedImplicitly]
public static class WebhookEventTypes
{
public static IEnumerable<WebhookEventDescriptor> GetWebhookEventDescriptors()
{
yield return GetWebhookEventDescriptor("content-item.created", "Created", typeof(ContentItemEventPayload));
yield return GetWebhookEventDescriptor("content-item.published", "Published", typeof(ContentItemEventPayload));
yield return GetWebhookEventDescriptor("content-item.unpublished", "Unpublished", typeof(ContentItemEventPayload));
yield return GetWebhookEventDescriptor("content-item.removed", "Removed", typeof(ContentItemEventPayload));
}
private static WebhookEventDescriptor GetWebhookEventDescriptor(string webhookEventType, string eventType, Type payloadType)
{
return new WebhookEventDescriptor
{
WebhookEventType = webhookEventType,
EventType = eventType,
PayloadType = payloadType
};
}
}

View file

@ -1,8 +0,0 @@
namespace Elsa.OrchardCore.WebhookPayloads;
public record ContentItemEventPayload(
string ContentType,
string DisplayText,
string Author,
string Owner,
string ContentItemId);