Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5424aae
Marked empty/null actions as failed
hakimms Jul 10, 2023
f647edd
replaced tabs with spaces
hakimms Jul 10, 2023
0cee892
Updated spacing
hakimms Jul 10, 2023
b7b2811
Replaced tab with spaces
hakimms Jul 11, 2023
e340d15
Updates based on feedback
hakimms Jul 15, 2023
03d26bb
Updated Validation Logic
hakimms Jul 18, 2023
b619a92
WIP Added test cases for OneOrMoreRequiredAttribute
hakimms Jul 19, 2023
30cf830
Added test case for EnumberableItemsNotNull Attribute
hakimms Jul 19, 2023
f0ffd40
Feedback updates
hakimms Jul 19, 2023
d3201c3
Updates based on PR feedback
hakimms Jul 20, 2023
b55cad3
Sort 'using' lists and removed unapplicable data annotation
hakimms Jul 20, 2023
d746379
Adding necessary updates to merge with azure main
hakimms Jul 20, 2023
00e3555
Removed xUnit reference now that we are using NUnit tests
hakimms Jul 25, 2023
cccac62
removed Update-Snippets.ps1 changes and will rebase
hakimms Jul 25, 2023
f0057ca
Removing requirement for response object in request to not perform ea…
hakimms Jul 25, 2023
1bb04ec
Added null check and test for response before marking as failed
hakimms Jul 27, 2023
201466c
Updated exception to validation exception and corrected test
hakimms Jul 27, 2023
90b1200
Changed back to argument null exception
hakimms Jul 28, 2023
cad33d1
Added response validation exception type
hakimms Jul 28, 2023
c770602
Ran script to add partial class for ResponseValidationException
hakimms Jul 28, 2023
1b07b17
Update sdk/entra/Microsoft.Azure.WebJobs.Extensions.AuthenticationEve…
hakimms Aug 1, 2023
9748b34
Added period
hakimms Aug 1, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@

## 1.0.0-beta.4 (Unreleased)

### Features Added

### Breaking Changes

### Bugs Fixed

### Other Changes
- Updated ODataType signature - 2433332
- Empty or null response actions will throw a bad response - 2161553

## 1.0.0-beta.3 (2022-12-13)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ public enum RequestStatusType
Successful = 2,
ValidationError = 3,
}
public partial class ResponseValidationException : System.Exception
{
public ResponseValidationException(string message) { }
public ResponseValidationException(string message, System.Exception innerException) { }
}
}
namespace Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework
{
Expand All @@ -59,7 +64,6 @@ protected ActionableCloudEventResponse() { }
public abstract partial class ActionableResponse<T> : Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.AuthenticationEventResponse where T : Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.AuthenticationEventAction
{
protected ActionableResponse() { }
[System.ComponentModel.DataAnnotations.RequiredAttribute]
[System.Text.Json.Serialization.JsonPropertyNameAttribute("actions")]
public System.Collections.Generic.List<T> Actions { get { throw null; } set { } }
}
Expand Down Expand Up @@ -100,13 +104,12 @@ internal AuthenticationEventRequestBase() { }
public System.Threading.Tasks.Task<Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.AuthenticationEventResponse> Failed(System.Exception exception) { throw null; }
public override string ToString() { throw null; }
}
public abstract partial class AuthenticationEventRequest<TResponse, TData> : Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.AuthenticationEventRequestBase where TResponse : Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.AuthenticationEventResponse where TData : Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.AuthenticationEventData
public abstract partial class AuthenticationEventRequest<TResponse, TData> : Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.AuthenticationEventRequestBase where TResponse : Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.AuthenticationEventResponse, new() where TData : Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.AuthenticationEventData
{
internal AuthenticationEventRequest() { }
[System.ComponentModel.DataAnnotations.RequiredAttribute]
[System.Text.Json.Serialization.JsonPropertyNameAttribute("data")]
public TData Data { get { throw null; } set { } }
[System.ComponentModel.DataAnnotations.RequiredAttribute]
[System.Text.Json.Serialization.JsonPropertyNameAttribute("response")]
public TResponse Response { get { throw null; } set { } }
public override System.Threading.Tasks.Task<Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.AuthenticationEventResponse> Completed() { throw null; }
Expand All @@ -122,7 +125,7 @@ public abstract partial class CloudEventData : Microsoft.Azure.WebJobs.Extension
{
protected CloudEventData() { }
}
public abstract partial class CloudEventRequest<TResponse, TData> : Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.AuthenticationEventRequest<TResponse, TData> where TResponse : Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.AuthenticationEventResponse where TData : Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.CloudEventData
public abstract partial class CloudEventRequest<TResponse, TData> : Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.AuthenticationEventRequest<TResponse, TData> where TResponse : Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.AuthenticationEventResponse, new() where TData : Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.CloudEventData
{
internal CloudEventRequest() { }
[System.Text.Json.Serialization.JsonPropertyNameAttribute("oDataType")]
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@
<data name="Ex_No_Lock" xml:space="preserve">
<value>Could not get exclusive lock to the resource.</value>
</data>
<data name="Ex_Null_Action_Items" xml:space="preserve">
<value>Actions can not contain null items.</value>
</data>
<data name="Ex_OpenApi_Missing" xml:space="preserve">
<value>No associated open api document found for version.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public Task SetValueAsync(object result, CancellationToken cancellationToken)
{
if (result == null)
{
throw new ArgumentNullException(AuthenticationEventResource.Ex_Invalid_Return);
throw new ResponseValidationException(AuthenticationEventResource.Ex_Invalid_Return);
}

if (result is AuthenticationEventResponse action)
Expand All @@ -83,7 +83,7 @@ public Task SetValueAsync(object result, CancellationToken cancellationToken)
}
catch (Exception ex)
{
Response = Request.Failed(ex, !(ex is ValidationException)).Result;
Response = Request.Failed(ex, true).Result;
}

return Task.CompletedTask;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;

namespace Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents
{
/// <summary>
/// Exception class for response validations
/// </summary>
public class ResponseValidationException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="ResponseValidationException"/> class.
/// </summary>
/// <param name="message"></param>
public ResponseValidationException(string message)
: base(message) { }

/// <summary>
/// Initializes a new instance of the <see cref="ResponseValidationException"/> class.
/// </summary>
/// <param name="message"></param>
/// <param name="innerException"></param>
public ResponseValidationException(string message, Exception innerException)
: base(message, innerException) { }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,21 @@

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.Validators;

namespace Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework
{
/// <summary>And abstract class for responses that implements actions.</summary>
/// <typeparam name="T">Of type EventAction.</typeparam>
/// <seealso cref="AuthenticationEventAction" />
///

public abstract class ActionableResponse<T> : AuthenticationEventResponse where T : AuthenticationEventAction
{
/// <summary>Gets or sets the actions.</summary>
/// <value>The actions.</value>
[JsonPropertyName("actions")]
[Required]
[OneOrMoreRequired]
[EnumerableItemsNotNull]
public List<T> Actions { get; set; } = new List<T>();

/// <summary>Invalidates this instance.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ internal AuthenticationEventRequestBase CreateEventRequest(HttpRequestMessage re
// ResponseSchema ?? string.Empty,
ResponseTemplate ?? string.Empty);

responseInfo.SetValue(eventRequest, eventResponse);

if (args != null && args.Length != 0)
{
eventRequest.InstanceCreated(args);
Expand All @@ -76,6 +74,8 @@ internal AuthenticationEventRequestBase CreateEventRequest(HttpRequestMessage re
Helpers.ValidateGraph(eventRequest);
}

responseInfo.SetValue(eventRequest, eventResponse);

return eventRequest;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework
/// <typeparam name="TData">The EventData model related to the request.</typeparam>
/// <seealso cref="AuthenticationEventResponse" />
/// <seealso cref="AuthenticationEventData" />
public abstract class AuthenticationEventRequest<TResponse, TData> : AuthenticationEventRequestBase where TResponse : AuthenticationEventResponse where TData : AuthenticationEventData
public abstract class AuthenticationEventRequest<TResponse, TData> : AuthenticationEventRequestBase
where TResponse : AuthenticationEventResponse , new()
where TData : AuthenticationEventData
{
/// <summary>Initializes a new instance of the <see cref="AuthenticationEventRequest{T, K}" /> class.</summary>
/// <param name="request">The request.</param>
Expand All @@ -23,7 +25,6 @@ internal AuthenticationEventRequest(HttpRequestMessage request) : base(request)
/// <value>The response.</value>
///
[JsonPropertyName("response")]
[Required]
public TResponse Response { get; set; }

/// <summary>Gets or sets the related EventData model.</summary>
Expand Down Expand Up @@ -62,6 +63,11 @@ public async override Task<AuthenticationEventResponse> Completed()

internal override Task<AuthenticationEventResponse> Failed(Exception exception, bool internalError)
{
if (Response == null)
{
Response = new TResponse();
}

Response.MarkAsFailed(exception, internalError);
return Task.FromResult<AuthenticationEventResponse>((TResponse)Response);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the MIT License.

using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.Validators;
using System.ComponentModel.DataAnnotations;
using System.Net.Http;
using System.Text.Json.Serialization;

Expand All @@ -11,7 +10,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework
/// <summary>Abstract class that wraps any request that relies on cloud events.</summary>
/// <typeparam name="TResponse">The Cloud Event Response.</typeparam>
/// <typeparam name="TData">The Cloud Event Data.</typeparam>
public abstract class CloudEventRequest<TResponse, TData> : AuthenticationEventRequest<TResponse, TData> where TResponse : AuthenticationEventResponse where TData : CloudEventData
public abstract class CloudEventRequest<TResponse, TData> : AuthenticationEventRequest<TResponse, TData>
where TResponse : AuthenticationEventResponse, new()
where TData : CloudEventData
{
/// <summary>Gets or sets the source.</summary>
/// <value>The source.</value>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;

namespace Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.Validators
{
/// <summary>Validator to ensure that a type value is enumerable and contains no null items.</summary>
[AttributeUsage(AttributeTargets.Property)]
internal class EnumerableItemsNotNullAttribute : ValidationAttribute
{
/// <summary>Initializes a new instance of the <see cref="EnumerableItemsNotNullAttribute" /> class.</summary>
public EnumerableItemsNotNullAttribute()
: base(AuthenticationEventResource.Ex_Null_Action_Items)
{
}

/// <summary>Returns true if the value is not null, is IEnumerable and no items are null.</summary>
/// <param name="value">The value of the object to validate.</param>
/// <returns>true if the specified value is valid; otherwise, false.</returns>
public override bool IsValid(object value)
{
return value is not null
&& value is IEnumerable<object> obj
&& !obj.Any(x => x == null);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;

namespace Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.Validators
{
/// <summary>Validator to ensure that a type value is enumerable and contains at least one item.</summary>
[AttributeUsage(AttributeTargets.Property)]
internal class OneOrMoreRequiredAttribute : ValidationAttribute
{
/// <summary>Initializes a new instance of the <see cref="OneOrMoreRequiredAttribute" /> class.</summary>
public OneOrMoreRequiredAttribute()
: base(AuthenticationEventResource.Ex_No_Action)
{
}

/// <summary>Returns true if the value is not null, is IEnumerable and contains at lease one item.</summary>
/// <param name="value">The value of the object to validate.</param>
/// <returns>true if the specified value is valid; otherwise, false.</returns>
public override bool IsValid(object value)
{
return value is not null
&& value is IEnumerable<object> obj
&& obj.Any();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework.Validators;
using NUnit.Framework;

namespace Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Tests.Framework.Validators
{
[TestFixture]
/// <summary>
/// This class will test OneOrMoreRequiredAttribute
/// </summary>
public class EnumerableItemsNotNullAttributeTests
{
[Test]
[TestCaseSource(nameof(TestScenarios))]
public void EnumberableItemsNotNullIsValidWithTestCase(object testObject, string message, bool success)
{
DummyClass dummyObj = new() { Obj = testObject };

if (success == false)
{
Assert.Throws<ValidationException>(() => Validator.ValidateObject(dummyObj, new ValidationContext(dummyObj), true), AuthenticationEventResource.Ex_Null_Action_Items);
}
else
{
Assert.DoesNotThrow(() => Validator.ValidateObject(dummyObj, new ValidationContext(dummyObj), true));
}
}

/// <summary>
/// Class that holds the attribute we want to test
/// </summary>
private class DummyClass
{
[EnumerableItemsNotNull]
public object Obj { get; set; }
}

private static IEnumerable<object[]> TestScenarios()
{
#region Invalid
yield return new TestCaseStructure()
{
Test = null,
Message = "Testing null",
}.ToArray;
yield return new TestCaseStructure()
{
Test = new object(),
Message = "Testing object",
}.ToArray;
yield return new TestCaseStructure()
{
Test = new List<object>() { null },
Message = "Testing object list with null item",
}.ToArray;
yield return new TestCaseStructure()
{
Test = new List<object>() { new(), null, new() },
Message = "Testing object list with multiple items with one null",
}.ToArray;
yield return new TestCaseStructure()
{
Test = new object[1],
Message = "Testing single null item array",
}.ToArray;
#endregion

#region Valid
yield return new TestCaseStructure()
{
Test = new List<object>() { new(), new() },
Message = "Testing list of objects",
Success = true,
}.ToArray;
yield return new TestCaseStructure()
{
Test = new List<object>(),
Message = "Testing initialized object",
Success = true,
}.ToArray;
yield return new TestCaseStructure()
{
Test = new object[0],
Message = "Testing empty array",
Success = true,
}.ToArray;
#endregion
}
}
}
Loading