diff --git a/src/Auth0.ManagementApi/Models/Actions/Action.cs b/src/Auth0.ManagementApi/Models/Actions/Action.cs index bbaa8141e..0da3120dc 100644 --- a/src/Auth0.ManagementApi/Models/Actions/Action.cs +++ b/src/Auth0.ManagementApi/Models/Actions/Action.cs @@ -50,5 +50,21 @@ public class Action : ActionBase /// [JsonProperty("supported_triggers")] public IList SupportedTriggers { get; set; } + + /// + /// The fk reference to InstalledIntegration entity. + /// + [JsonProperty("installed_integration_id")] + public string InstalledIntegrationId { get; set; } + + /// + [JsonProperty("integration")] + public Integration Integration { get; set; } + + /// + /// The time when this action was built successfully. + /// + [JsonProperty("built_at")] + public DateTime BuiltAt { 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 index 66cace207..a44d41f82 100644 --- a/src/Auth0.ManagementApi/Models/Actions/ActionDependency.cs +++ b/src/Auth0.ManagementApi/Models/Actions/ActionDependency.cs @@ -18,5 +18,11 @@ public class ActionDependency /// [JsonProperty("version")] public string Version { get; set; } + + /// + /// Optional value used primarily for private non registries. + /// + [JsonProperty("registry_url")] + public string RegistryUrl { get; set; } } } \ No newline at end of file diff --git a/src/Auth0.ManagementApi/Models/Actions/ActionOption.cs b/src/Auth0.ManagementApi/Models/Actions/ActionOption.cs new file mode 100644 index 000000000..cfddbb91f --- /dev/null +++ b/src/Auth0.ManagementApi/Models/Actions/ActionOption.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + +namespace Auth0.ManagementApi.Models.Actions +{ + /// + /// Allowable options for this param. + /// + public class ActionOption + { + /// + /// The value of an option that will be used within the application. + /// + [JsonProperty("value")] + public string Value { get; set; } + + /// + /// The display value for an option that will be used within the application. + /// + [JsonProperty("label")] + public string Label { get; set; } + } +} \ No newline at end of file diff --git a/src/Auth0.ManagementApi/Models/Actions/ActionRequiredBase.cs b/src/Auth0.ManagementApi/Models/Actions/ActionRequiredBase.cs new file mode 100644 index 000000000..57f49ba0c --- /dev/null +++ b/src/Auth0.ManagementApi/Models/Actions/ActionRequiredBase.cs @@ -0,0 +1,61 @@ +using System.Collections; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Auth0.ManagementApi.Models.Actions +{ + public class ActionRequiredBase + { + /// + /// Possible values: [UNSPECIFIED, STRING] + /// + [JsonProperty("type")] + public string Type { get; set; } + + /// + /// Name of the parameter + /// + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// Flag for if this parameter is required + /// + [JsonProperty("required")] + public bool? Required { get; set; } + + /// + /// The temp flag for if this parameter is required(experimental; for Labs use only) + /// + [JsonProperty("optional")] + public bool? Optional { get; set; } + + /// + /// Short label for this parameter + /// + [JsonProperty("label")] + public string Label { get; set; } + + /// + /// Lengthier description for this parameter + /// + [JsonProperty("description")] + public string Description { get; set; } + + /// + /// Default value for this parameter + /// + [JsonProperty("default_value")] + public string DefaultValue { get; set; } + + /// + /// Placeholder text for this parameter + /// + [JsonProperty("placeholder")] + public string Placeholder { get; set; } + + /// + [JsonProperty("options")] + public IList Options { get; set; } + } +} \ No newline at end of file diff --git a/src/Auth0.ManagementApi/Models/Actions/ActionSemVer.cs b/src/Auth0.ManagementApi/Models/Actions/ActionSemVer.cs new file mode 100644 index 000000000..9825ae49c --- /dev/null +++ b/src/Auth0.ManagementApi/Models/Actions/ActionSemVer.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + +namespace Auth0.ManagementApi.Models.Actions +{ + /// + /// Denotes the major.minor version of an integration release + /// + public class ActionSemVer + { + /// + /// Major version of the semver + /// + [JsonProperty("major")] + public int Major { get; set; } + + /// + /// Minor version of the semver + /// + [JsonProperty("minor")] + public int Minor { 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 index a96370448..81cf4872a 100644 --- a/src/Auth0.ManagementApi/Models/Actions/ActionSupportedTrigger.cs +++ b/src/Auth0.ManagementApi/Models/Actions/ActionSupportedTrigger.cs @@ -1,4 +1,9 @@ -using Newtonsoft.Json; +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace Auth0.ManagementApi.Models.Actions { @@ -15,5 +20,32 @@ public class ActionSupportedTrigger /// [JsonProperty("version")] public string Version { get; set; } + + /// + /// Points to the trigger status. + /// + [JsonProperty("status")] + public string Status { get; set; } + + /// + /// Runtimes supported by the trigger. + /// + [JsonProperty("runtimes")] + public string[] Runtimes { get; set; } + + /// + /// Runtime that will be used when none is specified when creating an action. + /// + [JsonProperty("default_runtime")] + public string DefaultRuntime { get; set; } + + /// + [JsonProperty("compatible_triggers")] + public IList CompatibleTrigger { get; set; } + + /// + [JsonProperty("binding_policy")] + [JsonConverter(typeof(StringEnumConverter))] + public BindingPolicy BindingPolicy { get; set; } } } \ No newline at end of file diff --git a/src/Auth0.ManagementApi/Models/Actions/ActionsRequiredConfiguration.cs b/src/Auth0.ManagementApi/Models/Actions/ActionsRequiredConfiguration.cs new file mode 100644 index 000000000..3e785e7ce --- /dev/null +++ b/src/Auth0.ManagementApi/Models/Actions/ActionsRequiredConfiguration.cs @@ -0,0 +1,10 @@ +namespace Auth0.ManagementApi.Models.Actions +{ + /// + /// Declares all the necessary configuration fields for an integration to work. + /// + public class ActionsRequiredConfiguration : ActionRequiredBase + { + + } +} \ No newline at end of file diff --git a/src/Auth0.ManagementApi/Models/Actions/ActionsRequiredSecrets.cs b/src/Auth0.ManagementApi/Models/Actions/ActionsRequiredSecrets.cs new file mode 100644 index 000000000..7c72ccd8a --- /dev/null +++ b/src/Auth0.ManagementApi/Models/Actions/ActionsRequiredSecrets.cs @@ -0,0 +1,10 @@ +namespace Auth0.ManagementApi.Models.Actions +{ + /// + /// Declares all the necessary secrets for an integration to work. + /// + public class ActionsRequiredSecrets : ActionRequiredBase + { + + } +} \ No newline at end of file diff --git a/src/Auth0.ManagementApi/Models/Actions/BindingPolicy.cs b/src/Auth0.ManagementApi/Models/Actions/BindingPolicy.cs new file mode 100644 index 000000000..8b9c67b75 --- /dev/null +++ b/src/Auth0.ManagementApi/Models/Actions/BindingPolicy.cs @@ -0,0 +1,18 @@ +using System.Runtime.Serialization; + +namespace Auth0.ManagementApi.Models.Actions +{ + /// + /// In order to execute an Action, it must be bound to a trigger using a binding. trigger-bound means that + /// bindings are managed by the tenant. entity-bound means that the bindings are automatically managed by Auth0 + /// and other internal resources will control those bindings. Tenants cannot manage entity-bound bindings. + /// + public enum BindingPolicy + { + [EnumMember(Value = "trigger-bound")] + TriggerBound, + + [EnumMember(Value = "entity-bound")] + EntityBound + } +} \ No newline at end of file diff --git a/src/Auth0.ManagementApi/Models/Actions/CompatibleTrigger.cs b/src/Auth0.ManagementApi/Models/Actions/CompatibleTrigger.cs new file mode 100644 index 000000000..7e6ea7c9c --- /dev/null +++ b/src/Auth0.ManagementApi/Models/Actions/CompatibleTrigger.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json; + +namespace Auth0.ManagementApi.Models.Actions +{ + /// + /// Informs which other trigger supports the same event and api. + /// + public class CompatibleTrigger + { + /// + /// Possible values: [post-login, credentials-exchange, pre-user-registration, post-user-registration, + /// post-change-password, send-phone-message, custom-phone-provider, custom-email-provider, iga-approval, + /// iga-certification, iga-fulfillment-assignment, iga-fulfillment-execution, + /// password-reset-post-challenge, custom-token-exchange-beta] + /// An 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/CurrentRelease.cs b/src/Auth0.ManagementApi/Models/Actions/CurrentRelease.cs new file mode 100644 index 000000000..dde4765c8 --- /dev/null +++ b/src/Auth0.ManagementApi/Models/Actions/CurrentRelease.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Auth0.ManagementApi.Models.Actions +{ + public class CurrentRelease + { + /// + /// Id of the associated IntegrationRelease. + /// + [JsonProperty("id")] + public string Id { get; set; } + + /// + [JsonProperty("trigger")] + public Trigger Trigger { get; set; } + + /// + [JsonProperty("semver")] + public ActionSemVer SemVer { get; set; } + + /// + [JsonProperty("required_secrets")] + public IList RequiredSecrets { get; set; } + + /// + [JsonProperty("required_configuration")] + public IList RequiredConfigurations { get; set; } + } +} \ No newline at end of file diff --git a/src/Auth0.ManagementApi/Models/Actions/FeatureType.cs b/src/Auth0.ManagementApi/Models/Actions/FeatureType.cs new file mode 100644 index 000000000..78b824c87 --- /dev/null +++ b/src/Auth0.ManagementApi/Models/Actions/FeatureType.cs @@ -0,0 +1,28 @@ +using System.Runtime.Serialization; + +namespace Auth0.ManagementApi.Models.Actions +{ + /// + /// Represents the type of the integration. + /// + public enum FeatureType + { + [EnumMember(Value = "unspecified")] + Unspecified, + + [EnumMember(Value = "action")] + Action, + + [EnumMember(Value = "social_connection")] + SocialConnection, + + [EnumMember(Value = "log_stream")] + LogStream, + + [EnumMember(Value = "sso_integration")] + SsoIntegration, + + [EnumMember(Value = "sms_provider")] + SmsProvider, + } +} \ No newline at end of file diff --git a/src/Auth0.ManagementApi/Models/Actions/Integration.cs b/src/Auth0.ManagementApi/Models/Actions/Integration.cs new file mode 100644 index 000000000..c529d0041 --- /dev/null +++ b/src/Auth0.ManagementApi/Models/Actions/Integration.cs @@ -0,0 +1,88 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Auth0.ManagementApi.Models.Actions +{ + /// + /// Integration defines a self-contained functioning unit which partners + /// publish. A partner may create one or many of these integrations. + /// + public class Integration + { + /// + /// id is a system generated GUID. This same ID is designed to be federated in + /// all the applicable localities. + /// + [JsonProperty("id")] + public string Id { get; set; } + + /// + /// catalog_id refers to the ID in the marketplace catalog + /// + [JsonProperty("catalog_id")] + public string CatalogId { get; set; } + + /// + /// url_slug refers to the url_slug in the marketplace catalog + /// + [JsonProperty("url_slug")] + public string UrlSlug { get; set; } + + /// + /// partner_id is the foreign key reference to the partner account this + /// integration belongs to. + /// + [JsonProperty("partner_id")] + public string PartnerId { get; set; } + + /// + /// name is the integration name, which will be used for display purposes in + /// the marketplace. + /// + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// description adds more text for the integration name -- also relevant for + /// the marketplace listing. + /// + [JsonProperty("description")] + public string Description { get; set; } + + /// + /// short_description is the brief description of the integration, which is used for display purposes in cards + /// + [JsonProperty("short_description")] + public string ShortDescription { get; set; } + + [JsonProperty("logo")] + public string Logo { get; set; } + + /// + /// description adds more text for the integration name -- also relevant for + /// the marketplace listing. + /// + [JsonProperty("feature_type")] + [JsonConverter(typeof(StringEnumConverter))] + public FeatureType FeatureType { get; set; } + + [JsonProperty("terms_of_use_url")] + public string TermsOfUseUrl { get; set; } + + [JsonProperty("privacy_policy_url")] + public string PrivacyPolicyUrl { get; set; } + + [JsonProperty("public_support_link")] + public string PublicSupportLink { get; set; } + + [JsonProperty("current_release")] + public CurrentRelease CurrentRelease { get; set; } + + [JsonProperty("created_at")] + public DateTime? CreatedAt { get; set; } + + [JsonProperty("updated_at")] + public DateTime? UpdatedAt { 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 index 2b68c4c81..563b814ba 100644 --- a/src/Auth0.ManagementApi/Models/Actions/Trigger.cs +++ b/src/Auth0.ManagementApi/Models/Actions/Trigger.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace Auth0.ManagementApi.Models.Actions { @@ -37,5 +38,16 @@ public class Trigger /// [JsonProperty("status")] public string Status { get; set; } + + /// + /// Informs which other trigger supports the same event and api. + /// + [JsonProperty("compatible_triggers")] + public IList CompatibleTriggers { get; set; } + + /// + [JsonProperty("binding_policy")] + [JsonConverter(typeof(StringEnumConverter))] + public BindingPolicy BindingPolicy { get; set; } } } \ No newline at end of file diff --git a/tests/Auth0.ManagementApi.IntegrationTests/ActionsTests.cs b/tests/Auth0.ManagementApi.IntegrationTests/ActionsTests.cs index bebbcfc8b..d837aa20e 100644 --- a/tests/Auth0.ManagementApi.IntegrationTests/ActionsTests.cs +++ b/tests/Auth0.ManagementApi.IntegrationTests/ActionsTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Threading.Tasks; using Auth0.Core.Exceptions; @@ -10,6 +11,7 @@ using Auth0.ManagementApi.Paging; using FluentAssertions; using Xunit; +using Action = Auth0.ManagementApi.Models.Actions.Action; namespace Auth0.ManagementApi.IntegrationTests @@ -40,30 +42,35 @@ public ActionsTests(ActionsTestsFixture fixture) [Fact] public async Task Test_actions_crud_sequence() { - var actionsBeforeCreate = await fixture.ApiClient.Actions.GetAllAsync(new GetActionsRequest(), new PaginationInfo()); + var actionsBeforeCreate = + await fixture.ApiClient.Actions.GetAllAsync(new GetActionsRequest(), new PaginationInfo()); var createdAction = await fixture.ApiClient.Actions.CreateAsync(new CreateActionRequest { Name = $"{TestingConstants.ActionPrefix}-{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"} } + Secrets = new List { new ActionSecret { Name = "My_Secret", Value = "Test123" } }, + SupportedTriggers = new List + { new ActionSupportedTrigger { Id = "post-login", Version = "v2" } } }); fixture.TrackIdentifier(CleanUpType.Actions, createdAction.Id); - var actionsAfterCreate = await fixture.ApiClient.Actions.GetAllAsync(new GetActionsRequest(), new PaginationInfo()); + var actionsAfterCreate = + await fixture.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)); + createdAction.Should() + .BeEquivalentTo(actionsAfterCreate.Last(), options => options.Excluding(o => o.Status)); var updatedAction = await fixture.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.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 fixture.ApiClient.Actions.GetAsync(updatedAction.Id); @@ -73,7 +80,8 @@ public async Task Test_actions_crud_sequence() await fixture.ApiClient.Actions.DeleteAsync(actionAfterUpdate.Id); - var actionsAfterDelete = await fixture.ApiClient.Actions.GetAllAsync(new GetActionsRequest(), new PaginationInfo()); + var actionsAfterDelete = + await fixture.ApiClient.Actions.GetAllAsync(new GetActionsRequest(), new PaginationInfo()); actionsAfterDelete.Count.Should().Be(actionsBeforeCreate.Count); fixture.UnTrackIdentifier(CleanUpType.Actions, createdAction.Id); @@ -90,25 +98,29 @@ public async Task Test_get_triggers() [Fact] public async Task Test_get_and_update_trigger_bindings() { - var triggerBindingsBeforeCreate = await fixture.ApiClient.Actions.GetAllTriggerBindingsAsync("post-login", new PaginationInfo()); - + var triggerBindingsBeforeCreate = + await fixture.ApiClient.Actions.GetAllTriggerBindingsAsync("post-login", new PaginationInfo()); + var createdAction = await fixture.ApiClient.Actions.CreateAsync(new CreateActionRequest { Name = $"{TestingConstants.ActionPrefix}-{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" } } + SupportedTriggers = new List + { new ActionSupportedTrigger { Id = "post-login", Version = "v2" } } }); fixture.TrackIdentifier(CleanUpType.Actions, createdAction.Id); - await RetryUtils.Retry(() => fixture.ApiClient.Actions.GetAsync(createdAction.Id), response => response.Status != "built"); + await RetryUtils.Retry(() => fixture.ApiClient.Actions.GetAsync(createdAction.Id), + response => response.Status != "built"); await fixture.ApiClient.Actions.DeployAsync(createdAction.Id); // - await RetryUtils.Retry(() => fixture.ApiClient.Actions.GetAsync(createdAction.Id), response => !response.AllChangesDeployed); + await RetryUtils.Retry(() => fixture.ApiClient.Actions.GetAsync(createdAction.Id), + response => !response.AllChangesDeployed); await fixture.ApiClient.Actions.UpdateTriggerBindingsAsync("post-login", new UpdateTriggerBindingsRequest { @@ -126,7 +138,8 @@ public async Task Test_get_and_update_trigger_bindings() } }); - var triggerBindingsAfterCreate = await fixture.ApiClient.Actions.GetAllTriggerBindingsAsync("post-login", new PaginationInfo()); + var triggerBindingsAfterCreate = + await fixture.ApiClient.Actions.GetAllTriggerBindingsAsync("post-login", new PaginationInfo()); triggerBindingsAfterCreate.Count.Should().Be(triggerBindingsBeforeCreate.Count + 1); @@ -150,25 +163,29 @@ public async Task Test_action_version_crud_sequence() 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" } } + SupportedTriggers = new List + { new ActionSupportedTrigger { Id = "post-login", Version = "v2" } } }); fixture.TrackIdentifier(CleanUpType.Actions, createdAction.Id); // 2. Get all the versions after the action was created - var versionsAfterCreate = await fixture.ApiClient.Actions.GetAllVersionsAsync(createdAction.Id, new PaginationInfo()); - + var versionsAfterCreate = + await fixture.ApiClient.Actions.GetAllVersionsAsync(createdAction.Id, new PaginationInfo()); + versionsAfterCreate.Count.Should().Be(0); // 3.a Before deploying, ensure it's in built status (this is async and sometimes causes CI to fail) - await RetryUtils.Retry(() => fixture.ApiClient.Actions.GetAsync(createdAction.Id), (action) => action.Status != "built"); + await RetryUtils.Retry(() => fixture.ApiClient.Actions.GetAsync(createdAction.Id), + (action) => action.Status != "built"); // 3.b Deploy the current version await fixture.ApiClient.Actions.DeployAsync(createdAction.Id); // 4. Get all the versions after the action was deployed - var versionsAfterDeploy = await fixture.ApiClient.Actions.GetAllVersionsAsync(createdAction.Id, new PaginationInfo()); - + var versionsAfterDeploy = + await fixture.ApiClient.Actions.GetAllVersionsAsync(createdAction.Id, new PaginationInfo()); + versionsAfterDeploy.Count.Should().Be(1); // 5. Update the action @@ -178,7 +195,8 @@ public async Task Test_action_version_crud_sequence() }); // 6.a Before deploying, ensure it's in built status (this is async and sometimes causes CI to fail) - await RetryUtils.Retry(() => fixture.ApiClient.Actions.GetAsync(createdAction.Id), (action) => action.Status != "built"); + await RetryUtils.Retry(() => fixture.ApiClient.Actions.GetAsync(createdAction.Id), + (action) => action.Status != "built"); // 6.b. Deploy the latest version var deployedVersion = await fixture.ApiClient.Actions.DeployAsync(createdAction.Id); @@ -187,7 +205,8 @@ public async Task Test_action_version_crud_sequence() await Task.Delay(2000); // 7. Get all the versions after the action was updated - var versionsAfterSecondDeploy = await fixture.ApiClient.Actions.GetAllVersionsAsync(createdAction.Id, new PaginationInfo()); + var versionsAfterSecondDeploy = + await fixture.ApiClient.Actions.GetAllVersionsAsync(createdAction.Id, new PaginationInfo()); versionsAfterSecondDeploy.Count.Should().Be(2); versionsAfterSecondDeploy.Single(v => v.Id == deployedVersion.Id).Deployed.Should().BeTrue(); @@ -197,23 +216,84 @@ public async Task Test_action_version_crud_sequence() action.DeployedVersion.Id.Should().Be(deployedVersion.Id); // 9. Rollback - var rollbackedVersion = await fixture.ApiClient.Actions.RollbackToVersionAsync(createdAction.Id, versionsAfterDeploy.Single().Id); + var rollbackedVersion = + await fixture.ApiClient.Actions.RollbackToVersionAsync(createdAction.Id, + versionsAfterDeploy.Single().Id); // 10. Get all the versions after the action was rollbacked // Retry until the rollback was processed as this is async - var versionAfterRollback = await RetryUtils.Retry(() => fixture.ApiClient.Actions.GetVersionAsync(createdAction.Id, rollbackedVersion.Id), (response) => response.Deployed == false); + var versionAfterRollback = + await RetryUtils.Retry( + () => fixture.ApiClient.Actions.GetVersionAsync(createdAction.Id, rollbackedVersion.Id), + (response) => response.Deployed == false); - var versionsAfterRollback = await fixture.ApiClient.Actions.GetAllVersionsAsync(createdAction.Id, new PaginationInfo()); + var versionsAfterRollback = + await fixture.ApiClient.Actions.GetAllVersionsAsync(createdAction.Id, new PaginationInfo()); versionsAfterRollback.Count.Should().Be(3); - versionsAfterRollback.Single(v => v.Id == versionAfterRollback.Id).Should().BeEquivalentTo(versionAfterRollback); + 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()); + versionsAfterRollback.Where(v => v.Id != versionAfterRollback.Id).ToList() + .ForEach(v => v.Deployed.Should().BeFalse()); // 10. Delete Action await fixture.ApiClient.Actions.DeleteAsync(createdAction.Id); fixture.UnTrackIdentifier(CleanUpType.Actions, createdAction.Id); } + + [Fact] + public async Task Test_get_actions_with_integration_details() + { + var sampleGetActionsWithIntegrationData = + await File.ReadAllTextAsync("./Data/GetActionsResponseWithIntegrationData.json"); + var httpManagementClientConnection = new HttpClientManagementConnection(); + var actions = + httpManagementClientConnection.DeserializeContent(sampleGetActionsWithIntegrationData, + null); + + actions.Should().NotBeNull(); + + actions.Id.Should().Be("9be52336-3338-46fe-be43-3845ea874b16"); + actions.Name.Should().Be("post-login-action"); + actions.BuiltAt.Should().Be(DateTime.Parse("2024-10-28T11:53:00.811042526")); + actions.Status.Should().Be("building"); + + actions.SupportedTriggers.Should().NotBeEmpty(); + var supportedTrigger = actions.SupportedTriggers.First(); + supportedTrigger.Id.Should().Be("post-login"); + supportedTrigger.Version.Should().Be("v3"); + supportedTrigger.Status.Should().Be("built"); + supportedTrigger.Runtimes.Should().NotBeEmpty(); + supportedTrigger.DefaultRuntime.Should().Be("v18"); + supportedTrigger.CompatibleTrigger.Should().NotBeEmpty(); + supportedTrigger.CompatibleTrigger.First().Id.Should().Be("d929f92d-efd5-465f-9801-cdd40bfe2c55"); + supportedTrigger.CompatibleTrigger.First().Version.Should().Be("v2"); + supportedTrigger.BindingPolicy.Should().Be(BindingPolicy.TriggerBound); + + actions.AllChangesDeployed.Should().BeTrue(); + actions.CreatedAt.Should().Be(DateTime.Parse("2024-10-28T11:53:00.800362301")); + actions.UpdatedAt.Should().Be(DateTime.Parse("2024-10-28T11:53:00.811042526")); + + actions.Integration.Id.Should().Be("750ce7ba-eac5-44a2-97ac-a67b2183bdea"); + actions.Integration.CatalogId.Should().Be("auth0-country-based-access"); + actions.Integration.UrlSlug.Should().Be("country-based-access"); + actions.Integration.PartnerId.Should().Be("d929f92d-efd5-465f-9801-cdd40bfe2c39"); + actions.Integration.Name.Should().Be("Country-based Access"); + actions.Integration.Description.Should().Be("This integration allows you to restrict access to your applications by country. You may choose to implement Country-based Access controls for various reasons, including to allow your applications to comply with unique restrictions based on where you do business. \n\nWith the Country-based Access integration, you can define any and all countries to restrict persons and entities from those countries logging into your applications. "); + actions.Integration.ShortDescription.Should().Be("Restrict access to users by country"); + actions.Integration.Logo.Should().Be("https://cdn.auth0.com/marketplace/catalog/content/assets/creators/auth0/auth0-avatar.png"); + actions.Integration.FeatureType.Should().Be(FeatureType.Action); + actions.Integration.TermsOfUseUrl.Should().Be("https://cdn.auth0.com/website/legal/files/mktplace/auth0-integration.pdf"); + actions.Integration.PublicSupportLink.Should().Be("https://support.auth0.com/"); + + actions.Integration.CurrentRelease.Id.Should().Be("d929f92d-efd5-465f-9801-cdd40bfe2c61"); + actions.Integration.CurrentRelease.SemVer.Major.Should().Be(8); + actions.Integration.CurrentRelease.SemVer.Minor.Should().Be(1); + actions.Integration.CurrentRelease.RequiredSecrets.Should().NotBeNull(); + actions.Integration.CurrentRelease.RequiredConfigurations.Should().NotBeNull(); + + } } } diff --git a/tests/Auth0.ManagementApi.IntegrationTests/Auth0.ManagementApi.IntegrationTests.csproj b/tests/Auth0.ManagementApi.IntegrationTests/Auth0.ManagementApi.IntegrationTests.csproj index 4fec364de..eddfbb574 100644 --- a/tests/Auth0.ManagementApi.IntegrationTests/Auth0.ManagementApi.IntegrationTests.csproj +++ b/tests/Auth0.ManagementApi.IntegrationTests/Auth0.ManagementApi.IntegrationTests.csproj @@ -20,6 +20,9 @@ Always + + Always + diff --git a/tests/Auth0.ManagementApi.IntegrationTests/Data/GetActionsResponseWithIntegrationData.json b/tests/Auth0.ManagementApi.IntegrationTests/Data/GetActionsResponseWithIntegrationData.json new file mode 100644 index 000000000..bf92e0e45 --- /dev/null +++ b/tests/Auth0.ManagementApi.IntegrationTests/Data/GetActionsResponseWithIntegrationData.json @@ -0,0 +1,137 @@ +{ + "id": "9be52336-3338-46fe-be43-3845ea874b16", + "name": "post-login-action", + "supported_triggers": [ + { + "id": "post-login", + "version": "v3", + "status": "built", + "runtimes": [ + "v18", + "v19", + "v20" + ], + "default_runtime": "v18", + "compatible_triggers": [ + { + "id": "d929f92d-efd5-465f-9801-cdd40bfe2c55", + "version": "v2" + } + ], + "binding_policy": "trigger-bound" + } + ], + "created_at": "2024-10-28T11:53:00.800362301Z", + "updated_at": "2024-10-28T11:53:00.811042526Z", + "built_at": "2024-10-28T11:53:00.811042526Z", + "code": "// --- AUTH0 ACTIONS TEMPLATE https://github.com/auth0/opensource-marketplace/blob/main/templates/add-email-to-access-token-POST_LOGIN ---\n/**\n * Handler that will be called during the execution of a PostLogin flow.\n *\n * @param {Event} event - Details about the user and the context in which they are logging in.\n * @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.\n */\nexports.onExecutePostLogin = async (event, api) => {\n // This action adds the authenticated user's email address to the access token.\n\n let namespace = event.secrets.NAMESPACE || '';\n if (namespace && !namespace.endsWith('/')) {\n namespace += '/';\n }\n\n api.accessToken.setCustomClaim(namespace + 'email', event.user.email);\n};\n\n/**\n * Handler that will be invoked when this action is resuming after an external redirect. If your\n * onExecutePostLogin function does not perform a redirect, this function can be safely ignored.\n *\n * @param {Event} event - Details about the user and the context in which they are logging in.\n * @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.\n */\n// exports.onContinuePostLogin = async (event, api) => {\n// };\n", + "dependencies": [], + "runtime": "node18-actions", + "status": "building", + "secrets": [], + "current_version": { + "id": "51ab4488-998b-4956-808c-86cd0deb3663", + "code": "// --- AUTH0 ACTIONS TEMPLATE https://github.com/auth0/opensource-marketplace/blob/main/templates/add-email-to-access-token-POST_LOGIN ---\n/**\n * Handler that will be called during the execution of a PostLogin flow.\n *\n * @param {Event} event - Details about the user and the context in which they are logging in.\n * @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.\n */\nexports.onExecutePostLogin = async (event, api) => {\n // This action adds the authenticated user's email address to the access token.\n\n let namespace = event.secrets.NAMESPACE || '';\n if (namespace && !namespace.endsWith('/')) {\n namespace += '/';\n }\n\n api.accessToken.setCustomClaim(namespace + 'email', event.user.email);\n};\n\n/**\n * Handler that will be invoked when this action is resuming after an external redirect. If your\n * onExecutePostLogin function does not perform a redirect, this function can be safely ignored.\n *\n * @param {Event} event - Details about the user and the context in which they are logging in.\n * @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.\n */\n// exports.onContinuePostLogin = async (event, api) => {\n// };\n", + "runtime": "node18-actions", + "status": "BUILT", + "number": 1, + "build_time": "2024-10-28T11:53:22.299356405Z", + "created_at": "2024-10-28T11:53:22.202130877Z", + "updated_at": "2024-10-28T11:53:22.300515959Z" + }, + "deployed_version": { + "code": "// --- AUTH0 ACTIONS TEMPLATE https://github.com/auth0/opensource-marketplace/blob/main/templates/add-email-to-access-token-POST_LOGIN ---\n/**\n * Handler that will be called during the execution of a PostLogin flow.\n *\n * @param {Event} event - Details about the user and the context in which they are logging in.\n * @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.\n */\nexports.onExecutePostLogin = async (event, api) => {\n // This action adds the authenticated user's email address to the access token.\n\n let namespace = event.secrets.NAMESPACE || '';\n if (namespace && !namespace.endsWith('/')) {\n namespace += '/';\n }\n\n api.accessToken.setCustomClaim(namespace + 'email', event.user.email);\n};\n\n/**\n * Handler that will be invoked when this action is resuming after an external redirect. If your\n * onExecutePostLogin function does not perform a redirect, this function can be safely ignored.\n *\n * @param {Event} event - Details about the user and the context in which they are logging in.\n * @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.\n */\n// exports.onContinuePostLogin = async (event, api) => {\n// };\n", + "dependencies": [], + "id": "51ab4488-998b-4956-808c-86cd0deb3663", + "deployed": true, + "number": 1, + "built_at": "2024-10-28T11:53:22.299356405Z", + "secrets": [], + "status": "built", + "created_at": "2024-10-28T11:53:22.202130877Z", + "updated_at": "2024-10-28T11:53:22.300515959Z", + "runtime": "node18-actions", + "supported_triggers": [ + { + "id": "post-login", + "version": "v3" + } + ] + }, + "all_changes_deployed": true, + "installed_integration_id": "a758bb11-51fd-4e81-b707-e41f565fd046", + "integration": { + "id": "750ce7ba-eac5-44a2-97ac-a67b2183bdea", + "catalog_id": "auth0-country-based-access", + "url_slug": "country-based-access", + "partner_id": "d929f92d-efd5-465f-9801-cdd40bfe2c39", + "name": "Country-based Access", + "description": "This integration allows you to restrict access to your applications by country. You may choose to implement Country-based Access controls for various reasons, including to allow your applications to comply with unique restrictions based on where you do business. \n\nWith the Country-based Access integration, you can define any and all countries to restrict persons and entities from those countries logging into your applications. ", + "short_description": "Restrict access to users by country", + "logo": "https://cdn.auth0.com/marketplace/catalog/content/assets/creators/auth0/auth0-avatar.png", + "terms_of_use_url": "https://cdn.auth0.com/website/legal/files/mktplace/auth0-integration.pdf", + "public_support_link": "https://support.auth0.com/", + "updated_at": "2022-05-18T22:38:09.809749669Z", + "created_at": "2022-05-12T19:54:02.958669136Z", + "feature_type": "action", + "current_release": { + "id": "d929f92d-efd5-465f-9801-cdd40bfe2c61", + "semver": { + "major": 8, + "minor": 1 + }, + "trigger": { + "id": "d929f92d-edv5-465f-9801-cdd40bfe2c39", + "version": "v1", + "status": "built", + "runtimes": [ + "v18", + "v19", + "v20" + ], + "default_runtime": "v18", + "compatible_triggers": [ + { + "id": "d929f92d-efd5-465f-9801-cdd40bfe2c55", + "version": "v2" + } + ], + "binding_policy": "trigger-bound" + }, + "required_secrets": [ + { + "type": "STRING", + "name": "secret", + "required": true, + "optional": true, + "label": "short label", + "description": "lengthy description", + "placeholder": "placeholder text", + "options": [ + { + "value": "This value", + "label": "option label" + } + ] + } + ], + "required_configuration": [ + { + "type": "STRING", + "name": "secret", + "required": true, + "optional": true, + "label": "short label", + "description": "lengthy description", + "placeholder": "placeholder text", + "options": [ + { + "value": "This value", + "label": "option label" + } + ] + } + ] + } + } +}