Skip to content
This repository has been archived by the owner on Dec 12, 2023. It is now read-only.

Commit

Permalink
Add request value lookup binding (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
joachimdalen authored Oct 4, 2021
1 parent 1a4de2c commit 1ed583d
Show file tree
Hide file tree
Showing 27 changed files with 1,073 additions and 155 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ Bindings:

- [Query Parameter](./docs/query-parameter.md)
- [Multipart Request](./docs/multipart-request.md)
- [Request Value Lookup](./docs/request-value-lookup.md)
31 changes: 19 additions & 12 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,25 @@ stages:
command: "build"
arguments: "--no-restore --configuration $(BUILD_CONFIGURATION)"
projects: "**/*.csproj"
- task: DotNetCoreCLI@2
displayName: "Run Unit Tests"
inputs:
command: "test"
projects: "**/*Tests/*UnitTests.csproj"
arguments: '--configuration $(BUILD_CONFIGURATION) --collect "Code coverage"'
testRunTitle: "Unit Tests"
- task: DotNetCoreCLI@2
displayName: "Run Integration Tests"
env:
AFTU_RUN_AZURITE: true
AFTU_FUNC_APP_PATH: '../../../../Integration.FunctionApp/bin/Release/netcoreapp3.1'
AFTU_WRITE_LOG: ${{ parameters.WRITE_LOG }}
AFTU_AZURITE_SILENT: ${{ parameters.AZURITE_SILENT }}
inputs:
command: "test"
projects: "**/*Tests/*IntegrationTests.csproj"
arguments: '--configuration $(BUILD_CONFIGURATION) --collect "Code coverage"'
testRunTitle: "Integration Tests"
- task: PowerShell@2
displayName: 'Set package version'
name: SetVersion
Expand All @@ -88,18 +107,6 @@ stages:
nobuild: true
versioningScheme: 'byEnvVar'
versionEnvVar: 'SETVERSION_UPDATED_PACKAGE_VERSION'
- task: DotNetCoreCLI@2
displayName: "Run Tests"
env:
AFTU_RUN_AZURITE: true
AFTU_FUNC_APP_PATH: '../../../../Integration.FunctionApp/bin/Release/netcoreapp3.1'
AFTU_WRITE_LOG: ${{ parameters.WRITE_LOG }}
AFTU_AZURITE_SILENT: ${{ parameters.AZURITE_SILENT }}
inputs:
command: "test"
projects: "**/*Tests/*IntegrationTests.csproj"
arguments: '--configuration $(BUILD_CONFIGURATION) --collect "Code coverage"'
testRunTitle: "Integration Tests"
- task: Bash@3
condition: and(always(), eq(${{ parameters.WRITE_LOG }}, true))
inputs:
Expand Down
42 changes: 42 additions & 0 deletions docs/request-value-lookup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Request Value Lookup

Get a value from one of multiple places in a request. Currently supports:

- Query
- Header

## Attribute

| Name | Type | Description |
| -------- | ---------------------- | ----------------------------- |
| Location | `RequestValueLocation` | Location to check for value |
| Name | `string` | Priamary key name to look for |
| Aliases | `string[]` | Alternate key names |

## Example

```csharp
public static class VersionedHttpTrigger
{
[FunctionName(nameof(VersionedHttpTrigger))]
public static async Task<IActionResult> RunAsync(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "version")]
HttpRequest req, ILogger log, [
RequestValue(
Location = RequestValueLocation.Header | RequestValueLocation.Query,
Name = "apiVersion",
Aliases = new[] { "x-api-version" }
)]
string version
)
{
if (string.IsNullOrEmpty(version))
{
return new BadRequestResult();
}

log.LogInformation("Triggered for version {Version}", version);
return new OkObjectResult(version);
}
}
```
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Net;
using System.Net.Http;
using System.Net.Mime;
using System.Text;
Expand Down Expand Up @@ -48,5 +49,26 @@ public async Task MultipartHttpTrigger_ContentWithFiles_ReturnsValue()
Assert.AreEqual(jsonData.Username, model.Username);
Assert.That.BlobExists("multipart-files", "greeting.txt");
}

[TestMethod]
[StartFunctions(nameof(MultipartHttpTrigger))]
public async Task MultipartHttpTrigger_ContentWithoutFilesAndInvalidBody_ReturnsBadRequest()
{
var multipart = new MultipartFormDataContent();
var jsonData = new MultipartRequestBodyData
{
Email = "john@doe.local"
};
multipart.Add(new StringContent(JsonConvert.SerializeObject(jsonData), Encoding.UTF8,
MediaTypeNames.Application.Json));

var request = new HttpRequestMessage(HttpMethod.Post, "api/test/multipart")
{
Content = multipart
};

var response = await Fixture.Client.SendAsync(request);
Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Integration.FunctionApp.Functions;
using JoachimDalen.AzureFunctions.TestUtils.Attributes;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Integration.FunctionApp.IntegrationTests.Functions
{
[TestClass]
public class VersionedHttpTriggerTests : BaseFunctionTest
{
[TestMethod]
[StartFunctions(nameof(VersionedHttpTrigger))]
public async Task VersionedHttpTrigger_ValueInQuery_ReturnsValue()
{
var response = await Fixture.Client.GetAsync("/api/version?apiVersion=v1");
var responseBody = await response.Content.ReadAsStringAsync();

Assert.IsTrue(response.IsSuccessStatusCode);
Assert.AreEqual("v1", responseBody);
}

[TestMethod]
[StartFunctions(nameof(VersionedHttpTrigger))]
public async Task VersionedHttpTrigger_ValueInHeader_ReturnsValue()
{
var request = new HttpRequestMessage(HttpMethod.Get, "/api/version");
request.Headers.Add("x-api-version", "v2");
var response = await Fixture.Client.SendAsync(request);
var responseBody = await response.Content.ReadAsStringAsync();

Assert.IsTrue(response.IsSuccessStatusCode);
Assert.AreEqual("v2", responseBody);
}

[TestMethod]
[StartFunctions(nameof(VersionedHttpTrigger))]
public async Task VersionedHttpTrigger_ValueInQuery_ReturnsError()
{
var response = await Fixture.Client.GetAsync("/api/version");

Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
}
}
}
6 changes: 6 additions & 0 deletions src/Integration.FunctionApp/Functions/MultipartHttpTrigger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ public static async Task<IActionResult> RunAsync(
[Blob("multipart-files", FileAccess.Write)]
CloudBlobContainer blobContainer)
{
if (!multipartRequest.IsValid)
{
return new BadRequestObjectResult(multipartRequest.ValidationResults);
}


foreach (var requestFile in multipartRequest.Files)
{
var blob = blobContainer.GetBlockBlobReference(requestFile.FileName);
Expand Down
37 changes: 37 additions & 0 deletions src/Integration.FunctionApp/Functions/VersionedHttpTrigger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.Threading.Tasks;
using System.Web.Http;
using JoachimDalen.AzureFunctions.Extensions.Attributes;
using JoachimDalen.AzureFunctions.Extensions.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace Integration.FunctionApp.Functions
{
public static class VersionedHttpTrigger
{
[FunctionName("VersionedHttpTrigger")]
public static async Task<IActionResult> RunAsync(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "version")]
HttpRequest req, ILogger log, [
RequestValue(Location = RequestValueLocation.Header | RequestValueLocation.Query, Name = "apiVersion",
Aliases = new[]
{
"x-api-version"
})
]
string version
)
{
if (string.IsNullOrEmpty(version))
{
return new BadRequestResult();
}

log.LogInformation("Triggered for version {Version}", version);
return new OkObjectResult(version);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
namespace Integration.FunctionApp.Models
using System.ComponentModel.DataAnnotations;

namespace Integration.FunctionApp.Models
{
public class MultipartRequestBodyData
{
[Required]
public string Username { get; set; }

public string Email { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace JoachimDalen.AzureFunctions.Extensions.UnitTests
{
[TestClass]
public class HttpRequestExtensionsTests
{
[DataTestMethod]
[DataRow("form-data;name=\"file\"; filename=\"fileOne.png\"")]
[DataRow("form-data;name=\"file\"; filename=fileOne.png")]
public void GetEscapedContentDispositionFileName_Data_ReturnsCorrect(string contentDisposition)
{
var contentDispositionHeaderValue = ContentDispositionHeaderValue.Parse(contentDisposition);
Assert.AreEqual("fileOne.png", contentDispositionHeaderValue.GetEscapedContentDispositionFileName());
}

[TestMethod]
public async Task HasFiles_Files_ReturnsTrue()
{
var bytes = Encoding.UTF8.GetBytes("Hello");
var multipart = new MultipartFormDataContent
{
{new ByteArrayContent(bytes, 0, bytes.Length), "file", "greeting.txt"}
};

var mp = (await multipart.ReadAsMultipartAsync()).Contents.First();
Assert.IsTrue(mp.HasFiles("file"));
}

[TestMethod]
public async Task HasFiles_StringContent_ReturnsFalse()
{
var multipart = new MultipartFormDataContent
{
new StringContent("Hello", Encoding.UTF8, MediaTypeNames.Text.Plain)
};

var mp = (await multipart.ReadAsMultipartAsync()).Contents.First();
Assert.IsFalse(mp.HasFiles("file"));
}

[TestMethod]
public async Task HasData_JsonContent_ReturnsTrue()
{
var multipart = new MultipartFormDataContent
{
new StringContent("Hello", Encoding.UTF8, MediaTypeNames.Application.Json)
};

var mp = (await multipart.ReadAsMultipartAsync()).Contents.First();
Assert.IsTrue(mp.HasData());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="2.1.1" />
<PackageReference Include="coverlet.collector" Version="1.3.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\JoachimDalen.AzureFunctions.Extensions\JoachimDalen.AzureFunctions.Extensions.csproj" />
</ItemGroup>

</Project>
Loading

0 comments on commit 1ed583d

Please sign in to comment.