diff --git a/src/Auth0.ManagementApi/Clients/ActionsClient.cs b/src/Auth0.ManagementApi/Clients/ActionsClient.cs
new file mode 100644
index 000000000..bf5210795
--- /dev/null
+++ b/src/Auth0.ManagementApi/Clients/ActionsClient.cs
@@ -0,0 +1,253 @@
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using Auth0.ManagementApi.Models;
+using Auth0.ManagementApi.Models.Actions;
+using Auth0.ManagementApi.Paging;
+using Newtonsoft.Json;
+using Action = Auth0.ManagementApi.Models.Actions.Action;
+
+namespace Auth0.ManagementApi.Clients
+{
+ ///
+ /// Contains methods to access the /actions endpoints.
+ ///
+ public class ActionsClient : BaseClient
+ {
+ private const string ActionsBasePath = "actions";
+ private const string ActionsPath = "actions";
+ private const string TriggersPath = "triggers";
+ private const string ExecutionsPath = "executions";
+ private const string VersionsPath = "versions";
+ private const string BindingsPath = "bindings";
+ private const string DeployPath = "deploy";
+
+ private readonly JsonConverter[] _actionsConverters = { new PagedListConverter("actions") };
+ private readonly JsonConverter[] _triggersConverters = { new ListConverter("triggers") };
+ private readonly JsonConverter[] _versionsConverters = { new PagedListConverter("versions") };
+ private readonly JsonConverter[] _triggerBindingsConverters = { new PagedListConverter("bindings") };
+ private readonly JsonConverter[] _triggerBindingsListConverters = { new ListConverter("bindings") };
+
+ public ActionsClient(IManagementConnection connection, Uri baseUri, IDictionary defaultHeaders) : base(connection, baseUri, defaultHeaders)
+ {
+ }
+
+ ///
+ /// Retrieve all actions.
+ ///
+ /// Specifies criteria to use when querying actions.
+ /// Specifies pagination info to use.
+ /// The cancellation token to cancel operation.
+ /// An containing the actions.
+ public Task> GetAllAsync(GetActionsRequest request, PaginationInfo pagination, CancellationToken cancellationToken = default)
+ {
+ if (request == null)
+ throw new ArgumentNullException(nameof(request));
+ if (pagination == null)
+ throw new ArgumentNullException(nameof(pagination));
+
+ var queryStrings = new Dictionary
+ {
+ {"triggerId", request.TriggerId},
+ {"actionName", request.ActionName},
+ {"deployed", request.Deployed?.ToString()},
+ {"page", pagination.PageNo.ToString()},
+ {"per_page", pagination.PerPage.ToString()},
+ // Uncomment below once "include_totals" is supported.
+ // {"include_totals", pagination.IncludeTotals.ToString()},
+ {"installed", request.Installed?.ToString()},
+ };
+
+ return Connection.GetAsync>(BuildUri($"{ActionsBasePath}/{ActionsPath}", queryStrings), DefaultHeaders, _actionsConverters, cancellationToken);
+ }
+
+ ///
+ /// Retrieve an action by its ID.
+ ///
+ /// The ID of the action to retrieve.
+ /// The cancellation token to cancel operation.
+ /// The retrieved action.
+ public Task GetAsync(string id, CancellationToken cancellationToken = default)
+ {
+ return Connection.GetAsync(BuildUri($"{ActionsBasePath}/{ActionsPath}/{EncodePath(id)}"), DefaultHeaders, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// Create an action.
+ ///
+ ///
+ /// Once an action is created, it must be deployed, and then bound to a trigger before it will be executed as part of a flow.
+ ///
+ /// Specifies criteria to use when creating an action.
+ /// The cancellation token to cancel operation.
+ /// The new that has been created.
+ public Task CreateAsync(CreateActionRequest request, CancellationToken cancellationToken = default)
+ {
+ return Connection.SendAsync(HttpMethod.Post, BuildUri($"{ActionsBasePath}/{ActionsPath}"), request, DefaultHeaders, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// Update an existing action.
+ ///
+ ///
+ /// If this action is currently bound to a trigger, updating it will not affect any user flows until the action is deployed.
+ ///
+ /// The id of the action to update.
+ /// Specifies criteria to use when updating an action.
+ /// The cancellation token to cancel operation.
+ /// The that was updated.
+ public Task UpdateAsync(string id, UpdateActionRequest request, CancellationToken cancellationToken = default)
+ {
+ return Connection.SendAsync(new HttpMethod("PATCH"), BuildUri($"{ActionsBasePath}/{ActionsPath}/{EncodePath(id)}"), request, DefaultHeaders, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// Deletes an action and all of its associated versions.
+ ///
+ ///
+ /// An action must be unbound from all triggers before it can be deleted.
+ ///
+ /// The ID of the action to delete.
+ /// Specifies criteria to use when deleting an action.
+ /// The cancellation token to cancel operation.
+ /// A that represents the asynchronous delete operation.
+ public Task DeleteAsync(string id, DeleteActionRequest request = null, CancellationToken cancellationToken = default)
+ {
+ return Connection.SendAsync(HttpMethod.Delete, BuildUri($"{ActionsBasePath}/{ActionsPath}/{EncodePath(id)}"), request, DefaultHeaders, cancellationToken: cancellationToken);
+ }
+
+
+ ///
+ /// Retrieve the set of triggers currently available within actions. A trigger is an extensibility point to which actions can be bound
+ ///
+ /// The cancellation token to cancel operation.
+ /// A list containing the triggers.
+ public Task> GetAllTriggersAsync(CancellationToken cancellationToken = default)
+ {
+ return Connection.GetAsync>(BuildUri($"{ActionsBasePath}/{TriggersPath}"), DefaultHeaders, _triggersConverters, cancellationToken);
+ }
+
+ ///
+ /// Retrieve information about a specific execution of a trigger.
+ ///
+ ///
+ /// Relevant execution IDs will be included in tenant logs generated as part of that authentication flow.
+ /// Executions will only be stored for 10 days after their creation.
+ ///
+ /// The ID of the execution to retrieve.
+ /// The cancellation token to cancel operation.
+ /// The retrieved execution.
+ public Task GetExecutionAsync(string id, CancellationToken cancellationToken = default)
+ {
+ return Connection.GetAsync(BuildUri($"{ActionsBasePath}/{ExecutionsPath}/{EncodePath(id)}"), DefaultHeaders, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// Retrieve all versions of an action.
+ ///
+ ///
+ /// An action version is created whenever an action is deployed. An action version is immutable, once created.
+ ///
+ /// The ID of the action.
+ /// Specifies pagination info to use.
+ /// The cancellation token to cancel operation.
+ /// The retrieved versions of the specified action.
+ public Task> GetAllVersionsAsync(string actionId, PaginationInfo pagination, CancellationToken cancellationToken = default)
+ {
+ var queryStrings = new Dictionary
+ {
+ {"page", pagination.PageNo.ToString()},
+ {"per_page", pagination.PerPage.ToString()},
+ // {"include_totals", pagination.IncludeTotals.ToString()}
+ };
+
+ return Connection.GetAsync>(BuildUri($"{ActionsBasePath}/{ActionsPath}/{EncodePath(actionId)}/{VersionsPath}", queryStrings), DefaultHeaders, _versionsConverters, cancellationToken);
+ }
+
+ ///
+ /// Retrieve a specific version of an action.
+ ///
+ ///
+ /// An action version is created whenever an action is deployed. An action version is immutable, once created.
+ ///
+ /// The ID of the action.
+ /// The ID of the action version.
+ /// The cancellation token to cancel operation.
+ /// The retrieved version of the specified action.
+ public Task GetVersionAsync(string actionId, string versionId, CancellationToken cancellationToken = default)
+ {
+ return Connection.GetAsync(BuildUri($"{ActionsBasePath}/{ActionsPath}/{EncodePath(actionId)}/{VersionsPath}/{EncodePath(versionId)}"), DefaultHeaders, cancellationToken: cancellationToken);
+ }
+
+
+ ///
+ /// Retrieve the actions that are bound to a trigger.
+ ///
+ ///
+ /// Once an action is created and deployed, it must be attached (i.e. bound) to a trigger so that it will be executed as part of a flow.
+ /// The list of actions returned reflects the order in which they will be executed during the appropriate flow.
+ ///
+ /// An actions extensibility point.
+ /// Specifies pagination info to use.
+ /// The cancellation token to cancel operation.
+ /// The retrieved trigger bindings.
+ public Task> GetAllTriggerBindingsAsync(string triggerId, PaginationInfo pagination, CancellationToken cancellationToken = default)
+ {
+ var queryStrings = new Dictionary
+ {
+ {"page", pagination.PageNo.ToString()},
+ {"per_page", pagination.PerPage.ToString()},
+ // {"include_totals", pagination.IncludeTotals.ToString()}
+ };
+
+ return Connection.GetAsync>(BuildUri($"{ActionsBasePath}/{TriggersPath}/{EncodePath(triggerId)}/{BindingsPath}", queryStrings), DefaultHeaders, _triggerBindingsConverters, cancellationToken);
+ }
+
+ ///
+ /// Update the actions that are bound (i.e. attached) to a trigger.
+ /// Once an action is created and deployed, it must be attached(i.e.bound) to a trigger so that it will be executed as part of a flow.
+ /// The order in which the actions are provided will determine the order in which they are executed.
+ ///
+ /// An actions extensibility point.
+ /// Specifies criteria to use when updating the trigger bindings.
+ /// The cancellation token to cancel operation.
+ /// The trigger bindings.
+ public Task> UpdateTriggerBindingsAsync(string triggerId, UpdateTriggerBindingsRequest request, CancellationToken cancellationToken = default)
+ {
+ return Connection.SendAsync>(new HttpMethod("PATCH"), BuildUri($"{ActionsBasePath}/{TriggersPath}/{EncodePath(triggerId)}/{BindingsPath}"), request, DefaultHeaders, null, _triggerBindingsListConverters, cancellationToken);
+ }
+
+ ///
+ /// Deploy an action.
+ ///
+ ///
+ /// Deploying an action will create a new immutable version of the action. If the action is currently bound to a trigger,
+ /// then the system will begin executing the newly deployed version of the action immediately.Otherwise, the action will only be executed as a part of a flow once it is bound to that flow.
+ ///
+ /// The ID of the action to deploy.
+ /// The cancellation token to cancel operation.
+ /// The action version that was created.
+ public Task DeployAsync(string id, CancellationToken cancellationToken = default)
+ {
+ return Connection.SendAsync(HttpMethod.Post, BuildUri($"{ActionsBasePath}/{ActionsPath}/{EncodePath(id)}/{DeployPath}"), null, DefaultHeaders, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// Performs the equivalent of a roll-back of an action to an earlier, specified version.
+ ///
+ ///
+ /// Creates a new, deployed action version that is identical to the specified version.
+ /// If this action is currently bound to a trigger, the system will begin executing the newly-created version immediately.
+ ///
+ /// The ID of the action.
+ /// The ID of the version to deploy.
+ /// The cancellation token to cancel operation.
+ /// A that represents the asynchronous delete operation.
+ public Task RollbackToVersionAsync(string actionId, string versionId, CancellationToken cancellationToken = default)
+ {
+ return Connection.SendAsync(HttpMethod.Post, BuildUri($"{ActionsBasePath}/{ActionsPath}/{EncodePath(actionId)}/versions/{versionId}/deploy"), new {}, DefaultHeaders, cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/src/Auth0.ManagementApi/HttpClientManagementConnection.cs b/src/Auth0.ManagementApi/HttpClientManagementConnection.cs
index bb7a7cf38..97476622e 100644
--- a/src/Auth0.ManagementApi/HttpClientManagementConnection.cs
+++ b/src/Auth0.ManagementApi/HttpClientManagementConnection.cs
@@ -62,9 +62,9 @@ public async Task GetAsync(Uri uri, IDictionary headers, J
}
///
- public async Task SendAsync(HttpMethod method, Uri uri, object body, IDictionary headers, IList files = null, CancellationToken cancellationToken = default)
+ public async Task SendAsync(HttpMethod method, Uri uri, object body, IDictionary headers, IList files = null, JsonConverter[] converters = null, CancellationToken cancellationToken = default)
{
- return await Retry(async () => await SendAsyncInternal(method, uri, body, headers, files, cancellationToken)).ConfigureAwait(false);
+ return await Retry(async () => await SendAsyncInternal(method, uri, body, headers, files, converters, cancellationToken)).ConfigureAwait(false);
}
private async Task GetAsyncInternal(Uri uri, IDictionary headers, JsonConverter[] converters = null, CancellationToken cancellationToken = default)
@@ -76,12 +76,12 @@ private async Task GetAsyncInternal(Uri uri, IDictionary h
}
}
- private async Task SendAsyncInternal(HttpMethod method, Uri uri, object body, IDictionary headers, IList files = null, CancellationToken cancellationToken = default)
+ private async Task SendAsyncInternal(HttpMethod method, Uri uri, object body, IDictionary headers, IList files = null, JsonConverter[] converters = null, CancellationToken cancellationToken = default)
{
using (var request = new HttpRequestMessage(method, uri) { Content = BuildMessageContent(body, files) })
{
ApplyHeaders(request.Headers, headers);
- return await SendRequest(request, cancellationToken: cancellationToken).ConfigureAwait(false);
+ return await SendRequest(request, converters, cancellationToken).ConfigureAwait(false);
}
}
diff --git a/src/Auth0.ManagementApi/IManagementConnection.cs b/src/Auth0.ManagementApi/IManagementConnection.cs
index 37e86eeda..f0b12731b 100644
--- a/src/Auth0.ManagementApi/IManagementConnection.cs
+++ b/src/Auth0.ManagementApi/IManagementConnection.cs
@@ -41,11 +41,12 @@ public interface IManagementConnection
/// otherwise containing the JSON representation of the object is expected.
/// Dictionary containing additional headers that may override the defaults.
/// Optional containing file contents to upload as a post.
+ /// Optional array of s used to deserialize the resulting .
/// The cancellation token to cancel operation.
/// representing the async operation containing response body as .
///
/// can only be specified if is a Dictionary%lt;string, object%gt;"/>.
///
- Task SendAsync(HttpMethod method, Uri uri, object body, IDictionary headers, IList files = null, CancellationToken cancellationToken = default);
+ Task SendAsync(HttpMethod method, Uri uri, object body, IDictionary headers, IList files = null, JsonConverter[] converters = null, CancellationToken cancellationToken = default);
}
}
diff --git a/src/Auth0.ManagementApi/ListConverter.cs b/src/Auth0.ManagementApi/ListConverter.cs
new file mode 100644
index 000000000..a6ea51e47
--- /dev/null
+++ b/src/Auth0.ManagementApi/ListConverter.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using Auth0.ManagementApi.Paging;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace Auth0.ManagementApi
+{
+ internal class ListConverter : JsonConverter
+ {
+ private readonly string _collectionFieldName;
+
+ public ListConverter(string collectionFieldName)
+ {
+ _collectionFieldName = collectionFieldName;
+ }
+
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override bool CanConvert(Type objectType)
+ {
+ return typeof(IList).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
+ }
+
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
+ JsonSerializer serializer)
+ {
+ if (reader.TokenType == JsonToken.StartObject)
+ {
+ JObject item = JObject.Load(reader);
+
+ if (item[_collectionFieldName] != null)
+ {
+ return item[_collectionFieldName].ToObject>(serializer);
+ }
+ }
+ else
+ {
+ JArray array = JArray.Load(reader);
+
+ var collection = array.ToObject>();
+
+ return new PagedList(collection);
+ }
+
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Auth0.ManagementApi/ManagementApiClient.cs b/src/Auth0.ManagementApi/ManagementApiClient.cs
index 7b70ab5f3..05fa064c9 100644
--- a/src/Auth0.ManagementApi/ManagementApiClient.cs
+++ b/src/Auth0.ManagementApi/ManagementApiClient.cs
@@ -16,6 +16,11 @@ public class ManagementApiClient : IDisposable
protected readonly IManagementConnection connection;
IDisposable connectionToDispose;
+ ///
+ /// Contains all the methods to call the /actions endpoints.
+ ///
+ public ActionsClient Actions { get; }
+
///
/// Contains all the methods to call the /blacklists/tokens endpoints.
///
@@ -151,6 +156,7 @@ public ManagementApiClient(string token, Uri baseUri, IManagementConnection mana
var defaultHeaders = CreateDefaultHeaders(token);
+ Actions = new ActionsClient(managementConnection, baseUri, defaultHeaders);
BlacklistedTokens = new BlacklistedTokensClient(managementConnection, baseUri, defaultHeaders);
Branding = new BrandingClient(managementConnection, baseUri, defaultHeaders);
ClientGrants = new ClientGrantsClient(managementConnection, baseUri, defaultHeaders);
diff --git a/src/Auth0.ManagementApi/Models/Actions/Action.cs b/src/Auth0.ManagementApi/Models/Actions/Action.cs
new file mode 100644
index 000000000..bbaa8141e
--- /dev/null
+++ b/src/Auth0.ManagementApi/Models/Actions/Action.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using Newtonsoft.Json;
+
+namespace Auth0.ManagementApi.Models.Actions
+{
+ ///
+ /// Represents an action in Auth0
+ ///
+ public class Action : ActionBase
+ {
+ ///
+ /// The unique ID of the action.
+ ///
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ ///
+ /// The build status of this action.
+ ///
+ [JsonProperty("status")]
+ public string Status { get; set; }
+
+ ///
+ /// The version of the action that is currently deployed.
+ ///
+ [JsonProperty("deployed_version")]
+ public ActionVersion DeployedVersion { get; set; }
+
+ ///
+ /// True if all of an Action's contents have been deployed.
+ ///
+ [JsonProperty("all_changes_deployed")]
+ public bool AllChangesDeployed { get; set; }
+
+ ///
+ /// The time when this action was created.
+ ///
+ [JsonProperty("created_at")]
+ public DateTime CreatedAt { get; set; }
+
+ ///
+ /// The time when this action was updated.
+ ///
+ [JsonProperty("updated_at")]
+ public DateTime UpdatedAt { get; set; }
+
+ ///
+ /// The list of triggers that this action supports.
+ ///
+ [JsonProperty("supported_triggers")]
+ public IList SupportedTriggers { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Auth0.ManagementApi/Models/Actions/ActionBase.cs b/src/Auth0.ManagementApi/Models/Actions/ActionBase.cs
new file mode 100644
index 000000000..6f2fc0c4a
--- /dev/null
+++ b/src/Auth0.ManagementApi/Models/Actions/ActionBase.cs
@@ -0,0 +1,38 @@
+using System.Collections.Generic;
+using Newtonsoft.Json;
+
+namespace Auth0.ManagementApi.Models.Actions
+{
+ public abstract class ActionBase
+ {
+ ///
+ /// The name of an action.
+ ///
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ ///
+ /// The source code of the action.
+ ///
+ [JsonProperty("code")]
+ public string Code { get; set; }
+
+ ///
+ /// The list of third party npm modules, and their versions, that this action depends on.
+ ///
+ [JsonProperty("dependencies")]
+ public IList Dependencies { get; set; }
+
+ ///
+ /// The Node runtime. For example: node12, defaults to node12
+ ///
+ [JsonProperty("runtime")]
+ public string Runtime { get; set; }
+
+ ///
+ /// The list of secrets that are included in an action or a version of an action.
+ ///
+ [JsonProperty("secrets")]
+ public IList Secrets { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Auth0.ManagementApi/Models/Actions/ActionDependency.cs b/src/Auth0.ManagementApi/Models/Actions/ActionDependency.cs
new file mode 100644
index 000000000..66cace207
--- /dev/null
+++ b/src/Auth0.ManagementApi/Models/Actions/ActionDependency.cs
@@ -0,0 +1,22 @@
+using Newtonsoft.Json;
+
+namespace Auth0.ManagementApi.Models.Actions
+{
+ ///
+ /// Represent an npm dependency for an action or an action's version.
+ ///
+ public class ActionDependency
+ {
+ ///
+ /// The name of the npm module, e.g. 'lodash'
+ ///
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ ///
+ /// The version of the npm module, e.g. '4.17.1'
+ ///
+ [JsonProperty("version")]
+ public string Version { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Auth0.ManagementApi/Models/Actions/ActionError.cs b/src/Auth0.ManagementApi/Models/Actions/ActionError.cs
new file mode 100644
index 000000000..db4db0889
--- /dev/null
+++ b/src/Auth0.ManagementApi/Models/Actions/ActionError.cs
@@ -0,0 +1,16 @@
+using Newtonsoft.Json;
+
+namespace Auth0.ManagementApi.Models.Actions
+{
+ public class ActionError
+ {
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ [JsonProperty("msg")]
+ public string Message { get; set; }
+
+ [JsonProperty("url")]
+ public string Url { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Auth0.ManagementApi/Models/Actions/ActionExecution.cs b/src/Auth0.ManagementApi/Models/Actions/ActionExecution.cs
new file mode 100644
index 000000000..a0a47f390
--- /dev/null
+++ b/src/Auth0.ManagementApi/Models/Actions/ActionExecution.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using Newtonsoft.Json;
+
+namespace Auth0.ManagementApi.Models.Actions
+{
+ public class ActionExecution
+ {
+ ///
+ /// Identifies a specific execution.
+ ///
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ ///
+ /// The actions extensibility point.
+ ///
+ [JsonProperty("trigger_id")]
+ public string TriggerId { get; set; }
+
+ ///
+ /// The overall status of an execution.
+ ///
+ [JsonProperty("status")]
+ public string Status { get; set; }
+
+ ///
+ /// Captures the results of a single action being executed.
+ ///
+ [JsonProperty("results")]
+ public IList Results { get; set; }
+
+ ///
+ /// The time that the execution was started.
+ ///
+ [JsonProperty("created_at")]
+ public DateTime CreatedAt { get; set; }
+
+ ///
+ /// The time that the execution finished executing.
+ ///
+ [JsonProperty("updated_at")]
+ public DateTime UpdatedAt { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Auth0.ManagementApi/Models/Actions/ActionExecutionResult.cs b/src/Auth0.ManagementApi/Models/Actions/ActionExecutionResult.cs
new file mode 100644
index 000000000..95041112b
--- /dev/null
+++ b/src/Auth0.ManagementApi/Models/Actions/ActionExecutionResult.cs
@@ -0,0 +1,29 @@
+using System;
+using Newtonsoft.Json;
+
+namespace Auth0.ManagementApi.Models.Actions
+{
+ ///
+ /// Captures the results of a single action being executed.
+ ///
+ public class ActionExecutionResult
+ {
+ ///
+ /// The name of the action that was executed.
+ ///
+ [JsonProperty("action_name")]
+ public string ActionName { get; set; }
+
+ ///
+ /// The time when the action was started.
+ ///
+ [JsonProperty("started_at")]
+ public DateTime StartedAt { get; set; }
+
+ ///
+ /// The time when the action finished executing.
+ ///
+ [JsonProperty("ended_at")]
+ public DateTime EndedAt { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Auth0.ManagementApi/Models/Actions/ActionSecret.cs b/src/Auth0.ManagementApi/Models/Actions/ActionSecret.cs
new file mode 100644
index 000000000..2662567fb
--- /dev/null
+++ b/src/Auth0.ManagementApi/Models/Actions/ActionSecret.cs
@@ -0,0 +1,26 @@
+using System;
+using Newtonsoft.Json;
+
+namespace Auth0.ManagementApi.Models.Actions
+{
+ public class ActionSecret
+ {
+ ///
+ /// The name of the particular secret, e.g. API_KEY.
+ ///
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ ///
+ /// The time when the secret was last updated.
+ ///
+ [JsonProperty("updated_at")]
+ public DateTime UpdatedAt { get; private set; }
+
+ ///
+ /// The value of the particular secret, e.g. secret123. A secret's value can only be set upon creation. A secret's value will never be returned by the API.
+ ///
+ [JsonProperty("value")]
+ public string Value { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Auth0.ManagementApi/Models/Actions/ActionSupportedTrigger.cs b/src/Auth0.ManagementApi/Models/Actions/ActionSupportedTrigger.cs
new file mode 100644
index 000000000..a96370448
--- /dev/null
+++ b/src/Auth0.ManagementApi/Models/Actions/ActionSupportedTrigger.cs
@@ -0,0 +1,19 @@
+using Newtonsoft.Json;
+
+namespace Auth0.ManagementApi.Models.Actions
+{
+ public class ActionSupportedTrigger
+ {
+ ///
+ /// The actions extensibility point
+ ///
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ ///
+ /// The version of a trigger. v1, v2, etc.
+ ///
+ [JsonProperty("version")]
+ public string Version { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Auth0.ManagementApi/Models/Actions/ActionVersion.cs b/src/Auth0.ManagementApi/Models/Actions/ActionVersion.cs
new file mode 100644
index 000000000..012ba8189
--- /dev/null
+++ b/src/Auth0.ManagementApi/Models/Actions/ActionVersion.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using Newtonsoft.Json;
+
+namespace Auth0.ManagementApi.Models.Actions
+{
+ ///
+ /// Represents a version for an action in Auth0
+ ///
+ public class ActionVersion
+ {
+ ///
+ /// The unique id of an action version.
+ ///
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ ///
+ /// The source code of this specific version of the action.
+ ///
+ [JsonProperty("code")]
+ public string Code { get; set; }
+
+ ///
+ /// The Node runtime. For example: `node12`
+ ///
+ [JsonProperty("runtime")]
+ public string Runtime { get; set; }
+
+ ///
+ /// The index of this version in list of versions for the action.
+ ///
+ [JsonProperty("number")]
+ public int Number { get; set; }
+
+ ///
+ /// Indicates if this specific version is the currently one deployed.
+ ///
+ [JsonProperty("deployed")]
+ public bool? Deployed { get; set; }
+
+ ///
+ /// The list of third party npm modules, and their versions, that this specific version depends on.
+ ///
+ [JsonProperty("dependencies")]
+ public IList Dependencies { get; set; }
+
+ ///
+ /// The build status of this specific version.
+ ///
+ [JsonProperty("status")]
+ public string Status { get; set; }
+
+ ///
+ /// The time when this version was created.
+ ///
+ [JsonProperty("created_at")]
+ public DateTime CreatedAt { get; set; }
+
+ ///
+ /// The time when a version was updated. Versions are never updated externally. Only Auth0 will update an action version as it is being built.
+ ///
+ [JsonProperty("updated_at")]
+ public DateTime UpdatedAt { get; set; }
+
+ ///
+ /// The action to which this version belongs.
+ ///
+ [JsonProperty("action")]
+ public Action Action { get; set; }
+
+ ///
+ /// The list of secrets that are included in the version.
+ ///
+ [JsonProperty("secrets")]
+ public IList Secrets { get; set; }
+
+ ///
+ /// Any errors that occurred while the version was being built.
+ ///
+ [JsonProperty("errors")]
+ public IList Errors { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Auth0.ManagementApi/Models/Actions/CreateActionRequest.cs b/src/Auth0.ManagementApi/Models/Actions/CreateActionRequest.cs
new file mode 100644
index 000000000..04baa1d22
--- /dev/null
+++ b/src/Auth0.ManagementApi/Models/Actions/CreateActionRequest.cs
@@ -0,0 +1,17 @@
+using System.Collections.Generic;
+using Newtonsoft.Json;
+
+namespace Auth0.ManagementApi.Models.Actions
+{
+ ///
+ /// Request configuration for creating an action.
+ ///
+ public class CreateActionRequest : ActionBase
+ {
+ ///
+ /// The list of triggers that this action supports. At this time, an action can only target a single trigger at a time.
+ ///
+ [JsonProperty("supported_triggers")]
+ public IList SupportedTriggers { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Auth0.ManagementApi/Models/Actions/DeleteActionRequest.cs b/src/Auth0.ManagementApi/Models/Actions/DeleteActionRequest.cs
new file mode 100644
index 000000000..40c762e0e
--- /dev/null
+++ b/src/Auth0.ManagementApi/Models/Actions/DeleteActionRequest.cs
@@ -0,0 +1,16 @@
+using Newtonsoft.Json;
+
+namespace Auth0.ManagementApi.Models.Actions
+{
+ ///
+ /// Request configuration for deleting an action.
+ ///
+ public class DeleteActionRequest
+ {
+ ///
+ /// Force action deletion detaching bindings
+ ///
+ [JsonProperty("force")]
+ public bool? Force { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Auth0.ManagementApi/Models/Actions/GetActionsRequest.cs b/src/Auth0.ManagementApi/Models/Actions/GetActionsRequest.cs
new file mode 100644
index 000000000..8ff59ad22
--- /dev/null
+++ b/src/Auth0.ManagementApi/Models/Actions/GetActionsRequest.cs
@@ -0,0 +1,28 @@
+namespace Auth0.ManagementApi.Models.Actions
+{
+ ///
+ /// Request configuration for retrieving all actions.
+ ///
+ public class GetActionsRequest
+ {
+ ///
+ /// An actions extensibility point.
+ ///
+ public string TriggerId { get; set; }
+
+ ///
+ /// The name of the action to retrieve.
+ ///
+ public string ActionName { get; set; }
+
+ ///
+ /// Optional filter to only retrieve actions that are deployed.
+ ///
+ public bool? Deployed { get; set; }
+
+ ///
+ /// Optional. When true, return only installed actions. When false, return only custom actions. Returns all actions by default.
+ ///
+ public bool? Installed { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Auth0.ManagementApi/Models/Actions/Trigger.cs b/src/Auth0.ManagementApi/Models/Actions/Trigger.cs
new file mode 100644
index 000000000..2b68c4c81
--- /dev/null
+++ b/src/Auth0.ManagementApi/Models/Actions/Trigger.cs
@@ -0,0 +1,41 @@
+using System.Collections.Generic;
+using Newtonsoft.Json;
+
+namespace Auth0.ManagementApi.Models.Actions
+{
+ ///
+ /// Represents a Trigger in Auth0
+ ///
+ public class Trigger
+ {
+ ///
+ /// The actions extensibility point.
+ ///
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ ///
+ /// Runtimes supported by this trigger.
+ ///
+ [JsonProperty("runtimes")]
+ public IList Runtimes { get; set; }
+
+ ///
+ /// Runtime that will be used when none is specified when creating an action.
+ ///
+ [JsonProperty("default_runtime")]
+ public string DefaultRuntime { get; set; }
+
+ ///
+ /// The version of a trigger. v1, v2, etc.
+ ///
+ [JsonProperty("version")]
+ public string Version { get; set; }
+
+ ///
+ /// The trigger's status.
+ ///
+ [JsonProperty("status")]
+ public string Status { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Auth0.ManagementApi/Models/Actions/TriggerBinding.cs b/src/Auth0.ManagementApi/Models/Actions/TriggerBinding.cs
new file mode 100644
index 000000000..8c6db023e
--- /dev/null
+++ b/src/Auth0.ManagementApi/Models/Actions/TriggerBinding.cs
@@ -0,0 +1,47 @@
+using System;
+using Newtonsoft.Json;
+
+namespace Auth0.ManagementApi.Models.Actions
+{
+ ///
+ /// Represents a Trigger Binding in Auth0
+ ///
+ public class TriggerBinding
+ {
+ ///
+ /// The unique ID of this binding.
+ ///
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ ///
+ /// The actions extensibility point.
+ ///
+ [JsonProperty("trigger_id")]
+ public string TriggerId { get; set; }
+
+ ///
+ /// The connected action.
+ ///
+ [JsonProperty("action")]
+ public Action Action { get; set; }
+
+ ///
+ /// The time when the binding was created.
+ ///
+ [JsonProperty("created_at")]
+ public DateTime CreatedAt { get; set; }
+
+ ///
+ /// The time when the binding was updated.
+ ///
+ [JsonProperty("updated_at")]
+ public DateTime UpdatedAt { get; set; }
+
+ ///
+ /// The name of the binding.
+ ///
+ [JsonProperty("display_name")]
+ public string DisplayName { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Auth0.ManagementApi/Models/Actions/UpdateActionRequest.cs b/src/Auth0.ManagementApi/Models/Actions/UpdateActionRequest.cs
new file mode 100644
index 000000000..dd83599a3
--- /dev/null
+++ b/src/Auth0.ManagementApi/Models/Actions/UpdateActionRequest.cs
@@ -0,0 +1,10 @@
+namespace Auth0.ManagementApi.Models.Actions
+{
+ ///
+ /// Request configuration for updating an action.
+ ///
+ public class UpdateActionRequest: ActionBase
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/src/Auth0.ManagementApi/Models/Actions/UpdateTriggerBindingEntry.cs b/src/Auth0.ManagementApi/Models/Actions/UpdateTriggerBindingEntry.cs
new file mode 100644
index 000000000..fca875801
--- /dev/null
+++ b/src/Auth0.ManagementApi/Models/Actions/UpdateTriggerBindingEntry.cs
@@ -0,0 +1,34 @@
+using Newtonsoft.Json;
+
+namespace Auth0.ManagementApi.Models.Actions
+{
+ public class UpdateTriggerBindingEntry
+ {
+ public class BindingRef
+ {
+ ///
+ /// How the action is being referred to: `action_id` or `action_name`.
+ ///
+ [JsonProperty("type")]
+ public string Type { get; set; }
+
+ ///
+ /// The id or name of an action that is being bound to a trigger.
+ ///
+ [JsonProperty("value")]
+ public string Value { get; set; }
+ }
+
+ ///
+ /// A reference to an action. An action can be referred to by ID or by Name.
+ ///
+ [JsonProperty("ref")]
+ public BindingRef Ref { get; set; }
+
+ ///
+ /// The name of the binding.
+ ///
+ [JsonProperty("display_name")]
+ public string DisplayName { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Auth0.ManagementApi/Models/Actions/UpdateTriggerBindingsRequest.cs b/src/Auth0.ManagementApi/Models/Actions/UpdateTriggerBindingsRequest.cs
new file mode 100644
index 000000000..8efc71038
--- /dev/null
+++ b/src/Auth0.ManagementApi/Models/Actions/UpdateTriggerBindingsRequest.cs
@@ -0,0 +1,17 @@
+using System.Collections.Generic;
+using Newtonsoft.Json;
+
+namespace Auth0.ManagementApi.Models.Actions
+{
+ ///
+ /// Request configuration to update the actions that are bound (i.e. attached) to a trigger.
+ ///
+ public class UpdateTriggerBindingsRequest
+ {
+ ///
+ /// The actions that will be bound to this trigger. The order in which they are included will be the order in which they are executed.
+ ///
+ [JsonProperty("bindings")]
+ public IList Bindings { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/tests/Auth0.ManagementApi.IntegrationTests/ActionsTests.cs b/tests/Auth0.ManagementApi.IntegrationTests/ActionsTests.cs
new file mode 100644
index 000000000..cb1a16335
--- /dev/null
+++ b/tests/Auth0.ManagementApi.IntegrationTests/ActionsTests.cs
@@ -0,0 +1,180 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Auth0.ManagementApi.Models.Actions;
+using Auth0.ManagementApi.Paging;
+using Auth0.Tests.Shared;
+using FluentAssertions;
+using Xunit;
+
+namespace Auth0.ManagementApi.IntegrationTests
+{
+ public class ActionsTests : TestBase, IAsyncLifetime
+ {
+ private ManagementApiClient _apiClient;
+ public async Task InitializeAsync()
+ {
+ string token = await GenerateManagementApiToken();
+
+ _apiClient = new ManagementApiClient(token, GetVariable("AUTH0_MANAGEMENT_API_URL"), new HttpClientManagementConnection(options: new HttpClientManagementConnectionOptions { NumberOfHttpRetries = 9 }));
+ }
+
+ public Task DisposeAsync()
+ {
+ _apiClient.Dispose();
+ return Task.CompletedTask;
+ }
+
+ [Fact]
+ public async Task Test_actions_crud_sequence()
+ {
+ var actionsBeforeCreate = await _apiClient.Actions.GetAllAsync(new GetActionsRequest(), new PaginationInfo());
+
+ var createdAction = await _apiClient.Actions.CreateAsync(new CreateActionRequest
+ {
+ Name = $"Int-Test-Action-{Guid.NewGuid()}",
+ Code = "module.exports = () => {}",
+ Runtime = "node12",
+ Secrets = new List { new ActionSecret { Name = "My_Secret", Value = "Test123" } },
+ SupportedTriggers = new List { new ActionSupportedTrigger { Id = "post-login", Version = "v2"} }
+ });
+
+ var actionsAfterCreate = await _apiClient.Actions.GetAllAsync(new GetActionsRequest(), new PaginationInfo());
+
+ actionsAfterCreate.Count.Should().Be(actionsBeforeCreate.Count + 1);
+ createdAction.Should().BeEquivalentTo(actionsAfterCreate.Last(), options => options.Excluding(o => o.Status));
+
+ var updatedAction = await _apiClient.Actions.UpdateAsync(createdAction.Id, new UpdateActionRequest
+ {
+ Code = "module.exports = () => { console.log(true); }"
+ });
+
+ updatedAction.Should().BeEquivalentTo(createdAction, options => options.Excluding(o => o.Code).Excluding(o => o.UpdatedAt));
+ updatedAction.Code.Should().Be("module.exports = () => { console.log(true); }");
+
+ var actionAfterUpdate = await _apiClient.Actions.GetAsync(updatedAction.Id);
+
+ updatedAction.Should().BeEquivalentTo(actionAfterUpdate, options => options.Excluding(o => o.Status));
+ actionAfterUpdate.Code.Should().Be("module.exports = () => { console.log(true); }");
+
+ await _apiClient.Actions.DeleteAsync(actionAfterUpdate.Id);
+
+ var actionsAfterDelete = await _apiClient.Actions.GetAllAsync(new GetActionsRequest(), new PaginationInfo());
+ actionsAfterDelete.Count.Should().Be(actionsBeforeCreate.Count);
+ }
+
+ [Fact]
+ public async Task Test_get_triggers()
+ {
+ var triggers = await _apiClient.Actions.GetAllTriggersAsync();
+
+ triggers.Should().NotBeEmpty();
+ }
+
+ [Fact]
+ public async Task Test_get_and_update_trigger_bindings()
+ {
+ var triggerBindingsBeforeCreate = await _apiClient.Actions.GetAllTriggerBindingsAsync("post-login", new PaginationInfo());
+
+ var createdAction = await _apiClient.Actions.CreateAsync(new CreateActionRequest
+ {
+ Name = $"Int-Test-Action-{Guid.NewGuid()}",
+ Code = "module.exports = () => {}",
+ Runtime = "node12",
+ Secrets = new List { new ActionSecret { Name = "My_Secret", Value = "Test123" } },
+ SupportedTriggers = new List { new ActionSupportedTrigger { Id = "post-login", Version = "v2" } }
+ });
+
+ await _apiClient.Actions.DeployAsync(createdAction.Id);
+
+ await _apiClient.Actions.UpdateTriggerBindingsAsync("post-login", new UpdateTriggerBindingsRequest
+ {
+ Bindings = new List
+ {
+ new UpdateTriggerBindingEntry
+ {
+ Ref = new UpdateTriggerBindingEntry.BindingRef
+ {
+ Type = "action_id",
+ Value = createdAction.Id
+ },
+ DisplayName = "My Action"
+ }
+ }
+ });
+
+ var triggerBindingsAfterCreate = await _apiClient.Actions.GetAllTriggerBindingsAsync("post-login", new PaginationInfo());
+
+ triggerBindingsAfterCreate.Count.Should().Be(triggerBindingsBeforeCreate.Count + 1);
+
+ await _apiClient.Actions.UpdateTriggerBindingsAsync("post-login", new UpdateTriggerBindingsRequest
+ {
+ Bindings = new List()
+ });
+
+ await _apiClient.Actions.DeleteAsync(createdAction.Id);
+ }
+
+ [Fact]
+ public async Task Test_action_version_crud_sequence()
+ {
+ // 1. Create a new Action
+ var createdAction = await _apiClient.Actions.CreateAsync(new CreateActionRequest
+ {
+ Name = $"Int-Test-Action-{Guid.NewGuid()}",
+ Code = "module.exports = () => {}",
+ Runtime = "node12",
+ Secrets = new List { new ActionSecret { Name = "My_Secret", Value = "Test123" } },
+ SupportedTriggers = new List { new ActionSupportedTrigger { Id = "post-login", Version = "v2" } }
+ });
+
+ // 2. Get all the versions after the action was created
+ var versionsAfterCreate = await _apiClient.Actions.GetAllVersionsAsync(createdAction.Id, new PaginationInfo());
+
+ versionsAfterCreate.Count.Should().Be(0);
+
+ // 3. Deploy the current version
+ await _apiClient.Actions.DeployAsync(createdAction.Id);
+
+ // 4. Get all the versions after the action was deployed
+ var versionsAfterDeploy = await _apiClient.Actions.GetAllVersionsAsync(createdAction.Id, new PaginationInfo());
+
+ versionsAfterDeploy.Count.Should().Be(1);
+
+ // 5. Update the action
+ await _apiClient.Actions.UpdateAsync(createdAction.Id, new UpdateActionRequest
+ {
+ Code = "module.exports = () => { console.log(true); }"
+ });
+
+ // 6. Deploy the latest version
+ var deployedVersion = await _apiClient.Actions.DeployAsync(createdAction.Id);
+
+ // 7. Get all the versions after the action was updated
+ var versionsAfterSecondDeploy = await _apiClient.Actions.GetAllVersionsAsync(createdAction.Id, new PaginationInfo());
+
+ versionsAfterSecondDeploy.Count.Should().Be(2);
+ versionsAfterSecondDeploy.Single(v => v.Id == deployedVersion.Id).Deployed.Should().BeTrue();
+ versionsAfterSecondDeploy.Single(v => v.Id != deployedVersion.Id).Deployed.Should().BeFalse();
+
+ var action = await _apiClient.Actions.GetAsync(createdAction.Id);
+ action.DeployedVersion.Id.Should().Be(deployedVersion.Id);
+
+ // 9. Rollback
+ var rollbackedVersion = await _apiClient.Actions.RollbackToVersionAsync(createdAction.Id, versionsAfterDeploy.Single().Id);
+
+ // 10. Get all the versions after the action was rollbacked
+ var versionsAfterRollback = await _apiClient.Actions.GetAllVersionsAsync(createdAction.Id, new PaginationInfo());
+ var versionAfterRollback = await _apiClient.Actions.GetVersionAsync(createdAction.Id, rollbackedVersion.Id);
+
+ versionsAfterRollback.Count.Should().Be(3);
+ versionsAfterRollback.Single(v => v.Id == versionAfterRollback.Id).Should().BeEquivalentTo(versionAfterRollback);
+ versionsAfterRollback.Single(v => v.Id == versionAfterRollback.Id).Deployed.Should().BeTrue();
+ versionsAfterRollback.Where(v => v.Id != versionAfterRollback.Id).ToList().ForEach(v => v.Deployed.Should().BeFalse());
+
+ // 10. Delete Action
+ await _apiClient.Actions.DeleteAsync(createdAction.Id);
+ }
+ }
+}
diff --git a/tests/Auth0.ManagementApi.IntegrationTests/ManagementApiClientTests.cs b/tests/Auth0.ManagementApi.IntegrationTests/ManagementApiClientTests.cs
index e0a086480..40bfa1018 100644
--- a/tests/Auth0.ManagementApi.IntegrationTests/ManagementApiClientTests.cs
+++ b/tests/Auth0.ManagementApi.IntegrationTests/ManagementApiClientTests.cs
@@ -57,7 +57,7 @@ public Task GetAsync(Uri uri, IDictionary headers = null,
return Task.FromResult(default(T));
}
- public Task SendAsync(HttpMethod method, Uri uri, object body, IDictionary headers = null, IList files = null, CancellationToken cancellationToken = default)
+ public Task SendAsync(HttpMethod method, Uri uri, object body, IDictionary headers = null, IList files = null, JsonConverter[] converters = null, CancellationToken cancellationToken = default)
{
return Task.FromResult(default(T));
}
@@ -104,7 +104,7 @@ public Task GetAsync(Uri uri, IDictionary headers = null,
return Task.FromResult(default(T));
}
- public Task SendAsync(HttpMethod method, Uri uri, object body, IDictionary headers = null, IList files = null, CancellationToken cancellationToken = default)
+ public Task SendAsync(HttpMethod method, Uri uri, object body, IDictionary headers = null, IList files = null, JsonConverter[] converters = null, CancellationToken cancellationToken = default)
{
LastHeaders = headers;
return Task.FromResult(default(T));