├── CHANGELOG.md
├── src
└── Fta.DemoFunc.Api
│ ├── Entities
│ ├── BaseEntity.cs
│ ├── Note.cs
│ └── AuditableEntity.cs
│ ├── Properties
│ ├── launchSettings.json
│ ├── serviceDependencies.json
│ └── serviceDependencies.local.json
│ ├── Interfaces
│ ├── IDateTimeProvider.cs
│ ├── INoteRepository.cs
│ ├── IDbConnectionFactory.cs
│ ├── INotificationService.cs
│ └── INoteService.cs
│ ├── Options
│ └── CreateNoteOptions.cs
│ ├── Contracts
│ ├── Requests
│ │ └── CreateNoteRequest.cs
│ └── Responses
│ │ └── CreateNoteResponse.cs
│ ├── Services
│ ├── DateTimeProvider.cs
│ ├── NotificationService.cs
│ └── NoteService.cs
│ ├── host.json
│ ├── Dtos
│ └── NoteDto.cs
│ ├── Models
│ └── NoteCreatedRequest.cs
│ ├── Persistence
│ ├── NpgsqlConnectionFactory.cs
│ ├── DatabaseInitializer.cs
│ └── Repositories
│ │ └── NoteRepository.cs
│ ├── Startup.cs
│ ├── Fta.DemoFunc.Api.csproj
│ ├── NotesFunction.cs
│ ├── DependencyInjection.cs
│ └── .gitignore
├── .github
├── CODE_OF_CONDUCT.md
├── ISSUE_TEMPLATE.md
└── PULL_REQUEST_TEMPLATE.md
├── LICENSE.md
├── tests
├── Fta.DemoFunc.Api.Tests.Unit
│ ├── Fta.DemoFunc.Api.Tests.Unit.csproj
│ └── NotesFunctionTests.cs
└── Fta.DemoFunc.Api.Tests.Integration
│ ├── Fta.DemoFunc.Api.Tests.Integration.csproj
│ ├── NotesFunctionFixture.cs
│ ├── NotificationApiServer.cs
│ ├── TestHelpers.cs
│ └── NotesFunctionTests.cs
├── .gitattributes
├── Fta.DemoFunc.sln
├── README.md
├── CONTRIBUTING.md
└── .gitignore
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [project-title] Changelog
2 |
3 |
4 | # x.y.z (yyyy-mm-dd)
5 |
6 | *Features*
7 | * ...
8 |
9 | *Bug Fixes*
10 | * ...
11 |
12 | *Breaking Changes*
13 | * ...
14 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Entities/BaseEntity.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Fta.DemoFunc.Api.Entities
4 | {
5 | public abstract class BaseEntity
6 | {
7 | public Guid Id { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "Fta.DemoFunc.Api": {
4 | "commandName": "Project",
5 | "commandLineArgs": "--port 7264",
6 | "launchBrowser": false
7 | }
8 | }
9 | }
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Interfaces/IDateTimeProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Fta.DemoFunc.Api.Interfaces
4 | {
5 | public interface IDateTimeProvider
6 | {
7 | DateTime Now { get; }
8 | DateTime UtcNow { get; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Entities/Note.cs:
--------------------------------------------------------------------------------
1 | namespace Fta.DemoFunc.Api.Entities
2 | {
3 | public class Note : AuditableEntity
4 | {
5 | public string Title { get; set; } = default!;
6 |
7 | public string Body { get; set; } = default!;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Properties/serviceDependencies.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "appInsights1": {
4 | "type": "appInsights"
5 | },
6 | "storage1": {
7 | "type": "storage",
8 | "connectionId": "AzureWebJobsStorage"
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Options/CreateNoteOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Fta.DemoFunc.Api.Options
2 | {
3 | public class CreateNoteOptions
4 | {
5 | public string Title { get; init; } = default!;
6 |
7 | public string Body { get; init; } = default!;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Properties/serviceDependencies.local.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "appInsights1": {
4 | "type": "appInsights.sdk"
5 | },
6 | "storage1": {
7 | "type": "storage.emulator",
8 | "connectionId": "AzureWebJobsStorage"
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Contracts/Requests/CreateNoteRequest.cs:
--------------------------------------------------------------------------------
1 | namespace Fta.DemoFunc.Api.Contracts.Requests
2 | {
3 | public class CreateNoteRequest
4 | {
5 | public string Title { get; init; } = default!;
6 |
7 | public string Body { get; init; } = default!;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Entities/AuditableEntity.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Fta.DemoFunc.Api.Entities
4 | {
5 | public abstract class AuditableEntity : BaseEntity
6 | {
7 | public DateTime CreatedAt { get; set; }
8 |
9 | public DateTime LastUpdatedOn { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Interfaces/INoteRepository.cs:
--------------------------------------------------------------------------------
1 | using Fta.DemoFunc.Api.Entities;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace Fta.DemoFunc.Api.Interfaces
6 | {
7 | public interface INoteRepository
8 | {
9 | Task CreateAsync(Note note, CancellationToken ct = default);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Services/DateTimeProvider.cs:
--------------------------------------------------------------------------------
1 | using Fta.DemoFunc.Api.Interfaces;
2 | using System;
3 |
4 | namespace Fta.DemoFunc.Api.Services
5 | {
6 | public class DateTimeProvider : IDateTimeProvider
7 | {
8 | public DateTime Now => DateTime.Now;
9 |
10 | public DateTime UtcNow => DateTime.UtcNow;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Interfaces/IDbConnectionFactory.cs:
--------------------------------------------------------------------------------
1 | using System.Data;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace Fta.DemoFunc.Api.Interfaces
6 | {
7 | public interface IDbConnectionFactory
8 | {
9 | public Task CreateConnectionAsync(CancellationToken ct = default);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Interfaces/INotificationService.cs:
--------------------------------------------------------------------------------
1 | using Fta.DemoFunc.Api.Options;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace Fta.DemoFunc.Api.Interfaces
6 | {
7 | public interface INotificationService
8 | {
9 | Task SendNoteCreatedEventAsync(CreateNoteOptions createNoteOptions, CancellationToken ct = default);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/host.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0",
3 | "logging": {
4 | "applicationInsights": {
5 | "samplingSettings": {
6 | "isEnabled": true,
7 | "excludedTypes": "Request"
8 | }
9 | },
10 | "logLevel": {
11 | "Fta.DemoFunc.Api.NotesFunction": "Information"
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Dtos/NoteDto.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Fta.DemoFunc.Api.Dtos
4 | {
5 | public class NoteDto
6 | {
7 | public string Id { get; set; } = default!;
8 |
9 | public string Title { get; set; } = default!;
10 |
11 | public string Body { get; set; } = default!;
12 |
13 | public DateTime LastUpdatedOn { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Models/NoteCreatedRequest.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 |
3 | namespace Fta.DemoFunc.Api.Models
4 | {
5 | public class NoteCreatedRequest
6 | {
7 | [JsonProperty("title")]
8 | public string Title { get; set; } = default!;
9 |
10 | [JsonProperty("description")]
11 | public string Description { get; set; } = default!;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Interfaces/INoteService.cs:
--------------------------------------------------------------------------------
1 | using Fta.DemoFunc.Api.Dtos;
2 | using Fta.DemoFunc.Api.Options;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | namespace Fta.DemoFunc.Api.Interfaces
7 | {
8 | public interface INoteService
9 | {
10 | Task CreateNoteAsync(CreateNoteOptions createNoteOptions, CancellationToken ct = default);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Contracts/Responses/CreateNoteResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Fta.DemoFunc.Api.Contracts.Responses
4 | {
5 | public class CreateNoteResponse
6 | {
7 | public string Id { get; set; } = default!;
8 |
9 | public string Title { get; set; } = default!;
10 |
11 | public string Body { get; set; } = default!;
12 |
13 | public DateTime LastUpdatedOn { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Microsoft Open Source Code of Conduct
2 |
3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
4 |
5 | Resources:
6 |
7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
10 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Persistence/NpgsqlConnectionFactory.cs:
--------------------------------------------------------------------------------
1 | using System.Data;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Fta.DemoFunc.Api.Interfaces;
5 | using Npgsql;
6 |
7 | namespace Fta.DemoFunc.Api.Persistence
8 | {
9 | public class NpgsqlConnectionFactory : IDbConnectionFactory
10 | {
11 | private readonly string _connectionString;
12 |
13 | public NpgsqlConnectionFactory(string connectionString)
14 | {
15 | _connectionString = connectionString;
16 | }
17 |
18 | public async Task CreateConnectionAsync(CancellationToken ct = default)
19 | {
20 | var connection = new NpgsqlConnection(_connectionString);
21 | await connection.OpenAsync(ct);
22 |
23 | return connection;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Startup.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Azure.Functions.Extensions.DependencyInjection;
2 | using Microsoft.Extensions.Configuration;
3 | using System;
4 |
5 | [assembly: FunctionsStartup(typeof(Fta.DemoFunc.Api.Startup))]
6 | namespace Fta.DemoFunc.Api
7 | {
8 | public class Startup : FunctionsStartup
9 | {
10 | public override void Configure(IFunctionsHostBuilder builder)
11 | {
12 | var configuration = new ConfigurationBuilder()
13 | .SetBasePath(Environment.CurrentDirectory)
14 | .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
15 | .AddEnvironmentVariables()
16 | .Build();
17 |
18 | builder.Services.AddApplication(configuration);
19 | builder.Services.AddInfrastructure(configuration);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Fta.DemoFunc.Api.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net6.0
4 | v4
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | PreserveNewest
16 |
17 |
18 | PreserveNewest
19 | Never
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
4 | > Please provide us with the following information:
5 | > ---------------------------------------------------------------
6 |
7 | ### This issue is for a: (mark with an `x`)
8 | ```
9 | - [ ] bug report -> please search issues before submitting
10 | - [ ] feature request
11 | - [ ] documentation issue or request
12 | - [ ] regression (a behavior that used to work and stopped in a new release)
13 | ```
14 |
15 | ### Minimal steps to reproduce
16 | >
17 |
18 | ### Any log messages given by the failure
19 | >
20 |
21 | ### Expected/desired behavior
22 | >
23 |
24 | ### OS and Version?
25 | > Windows 7, 8 or 10. Linux (which distribution). macOS (Yosemite? El Capitan? Sierra?)
26 |
27 | ### Versions
28 | >
29 |
30 | ### Mention any other details that might be useful
31 |
32 | > ---------------------------------------------------------------
33 | > Thanks! We'll be in touch soon.
34 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Persistence/DatabaseInitializer.cs:
--------------------------------------------------------------------------------
1 | using Dapper;
2 | using Fta.DemoFunc.Api.Interfaces;
3 | using System.Threading.Tasks;
4 |
5 | namespace Fta.DemoFunc.Api.Persistence
6 | {
7 | public class DatabaseInitializer
8 | {
9 | private readonly IDbConnectionFactory _connectionFactory;
10 |
11 | public DatabaseInitializer(IDbConnectionFactory connectionFactory)
12 | {
13 | _connectionFactory = connectionFactory;
14 | }
15 |
16 | public async Task InitializeAsync()
17 | {
18 | using var connection = await _connectionFactory.CreateConnectionAsync();
19 |
20 | await connection.ExecuteAsync(@"
21 | CREATE TABLE IF NOT EXISTS Notes (
22 | Id UUID PRIMARY KEY,
23 | Title VARCHAR(200) NOT NULL,
24 | Body VARCHAR(1000) NOT NULL,
25 | CreatedAt DATE NOT NULL,
26 | LastUpdatedOn DATE NOT NULL
27 | )"
28 | );
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Purpose
2 |
3 | * ...
4 |
5 | ## Does this introduce a breaking change?
6 |
7 | ```
8 | [ ] Yes
9 | [ ] No
10 | ```
11 |
12 | ## Pull Request Type
13 | What kind of change does this Pull Request introduce?
14 |
15 |
16 | ```
17 | [ ] Bugfix
18 | [ ] Feature
19 | [ ] Code style update (formatting, local variables)
20 | [ ] Refactoring (no functional changes, no api changes)
21 | [ ] Documentation content changes
22 | [ ] Other... Please describe:
23 | ```
24 |
25 | ## How to Test
26 | * Get the code
27 |
28 | ```
29 | git clone [repo-address]
30 | cd [repo-name]
31 | git checkout [branch-name]
32 | npm install
33 | ```
34 |
35 | * Test the code
36 |
37 | ```
38 | ```
39 |
40 | ## What to Check
41 | Verify that the following are valid
42 | * ...
43 |
44 | ## Other Information
45 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Microsoft Corporation.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE
--------------------------------------------------------------------------------
/tests/Fta.DemoFunc.Api.Tests.Unit/Fta.DemoFunc.Api.Tests.Unit.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 |
7 | false
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | runtime; build; native; contentfiles; analyzers; buildtransitive
18 | all
19 |
20 |
21 | runtime; build; native; contentfiles; analyzers; buildtransitive
22 | all
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/tests/Fta.DemoFunc.Api.Tests.Integration/Fta.DemoFunc.Api.Tests.Integration.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 |
7 | false
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 | all
20 |
21 |
22 | runtime; build; native; contentfiles; analyzers; buildtransitive
23 | all
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/tests/Fta.DemoFunc.Api.Tests.Integration/NotesFunctionFixture.cs:
--------------------------------------------------------------------------------
1 | using DotNet.Testcontainers.Builders;
2 | using System.Threading.Tasks;
3 | using Xunit;
4 | using Testcontainers.PostgreSql;
5 |
6 | namespace Fta.DemoFunc.Api.Tests.Integration
7 | {
8 | public class NotesFunctionFixture : IAsyncLifetime
9 | {
10 | private readonly PostgreSqlContainer _postgreSqlContainer =
11 | new PostgreSqlBuilder()
12 | .WithImage("postgres:latest")
13 | .WithEnvironment("POSTGRES_USER", "postgres")
14 | .WithEnvironment("POSTGRES_PASSWORD", "postgres")
15 | .WithEnvironment("POSTGRES_DB", "notesdb")
16 | .WithPortBinding(5432, 5432)
17 | .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(5432))
18 | .Build();
19 | private readonly NotificationApiServer _notificationApiServer = new();
20 |
21 | public string GetPostgresDbConnectionString() => _postgreSqlContainer.GetConnectionString();
22 | public string GetNotificationApiServerUrl() => _notificationApiServer.Url;
23 |
24 | public async Task DisposeAsync()
25 | {
26 | await _postgreSqlContainer.DisposeAsync();
27 | _notificationApiServer.Dispose();
28 | }
29 |
30 | public async Task InitializeAsync()
31 | {
32 | _notificationApiServer.Start();
33 | _notificationApiServer.SetupRequestDetails();
34 | await _postgreSqlContainer.StartAsync();
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Persistence/Repositories/NoteRepository.cs:
--------------------------------------------------------------------------------
1 | using Dapper;
2 | using Fta.DemoFunc.Api.Entities;
3 | using Fta.DemoFunc.Api.Interfaces;
4 | using System;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | namespace Fta.DemoFunc.Api.Persistence.Repositories
9 | {
10 | public class NoteRepository : INoteRepository
11 | {
12 | private readonly IDbConnectionFactory _connectionFactory;
13 | private readonly DatabaseInitializer _databaseInitializer;
14 |
15 | public NoteRepository(IDbConnectionFactory connectionFactory, DatabaseInitializer databaseInitializer)
16 | {
17 | _connectionFactory = connectionFactory;
18 | _databaseInitializer = databaseInitializer;
19 | }
20 |
21 | public async Task CreateAsync(Note note, CancellationToken ct = default)
22 | {
23 | await _databaseInitializer.InitializeAsync();
24 |
25 | using var connection = await _connectionFactory.CreateConnectionAsync(ct);
26 |
27 | var result = await connection.ExecuteAsync(
28 | @"
29 | INSERT INTO Notes (Id, Title, Body, CreatedAt, LastUpdatedOn)
30 | VALUES (@Id, @Title, @Body, @CreatedAt, @LastUpdatedOn)
31 | ",
32 | note
33 | );
34 |
35 | if (result == 1)
36 | {
37 | return note;
38 | }
39 |
40 | throw new ApplicationException("Could not save entity in db.");
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/tests/Fta.DemoFunc.Api.Tests.Integration/NotificationApiServer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using WireMock.RequestBuilders;
4 | using WireMock.ResponseBuilders;
5 | using WireMock.Server;
6 |
7 | namespace Fta.DemoFunc.Api.Tests.Integration
8 | {
9 | public class NotificationApiServer : IDisposable
10 | {
11 | private WireMockServer? _server;
12 |
13 | public string Url => _server!.Url!;
14 |
15 | public void Start()
16 | {
17 | _server = WireMockServer.Start();
18 | }
19 |
20 | public void SetupRequestDetails()
21 | {
22 | _server!.Given(Request.Create()
23 | .WithPath($"/notes")
24 | .UsingPost()
25 | .WithBody(GenerateRequestBody()))
26 | .RespondWith(Response.Create()
27 | .WithBody(GenerateResponseBody())
28 | .WithHeader("content-type", "application/json; charset=utf-8")
29 | .WithStatusCode(HttpStatusCode.Created));
30 | }
31 |
32 | public void Dispose()
33 | {
34 | _server!.Stop();
35 | _server.Dispose();
36 | }
37 |
38 | private static string GenerateRequestBody()
39 | {
40 | return $@"{{""title"":""Test note title"",""description"":""Test note description""}}";
41 | }
42 |
43 | private static string GenerateResponseBody()
44 | {
45 | return $@"{{""title"":""Test note title"",""description"":""Test note description"",""id"":""999""}}";
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Services/NotificationService.cs:
--------------------------------------------------------------------------------
1 | using Fta.DemoFunc.Api.Interfaces;
2 | using Fta.DemoFunc.Api.Models;
3 | using Fta.DemoFunc.Api.Options;
4 | using Microsoft.Extensions.Logging;
5 | using System;
6 | using System.Net.Http;
7 | using System.Text;
8 | using System.Text.Json;
9 | using System.Threading;
10 | using System.Threading.Tasks;
11 |
12 | namespace Fta.DemoFunc.Api.Services
13 | {
14 | public class NotificationService : INotificationService
15 | {
16 | private readonly HttpClient _httpClient;
17 | private readonly ILogger _logger;
18 |
19 | public NotificationService(HttpClient httpClient, ILogger logger)
20 | {
21 | _httpClient = httpClient;
22 | _logger = logger;
23 | }
24 |
25 | public async Task SendNoteCreatedEventAsync(CreateNoteOptions createNoteOptions, CancellationToken ct = default)
26 | {
27 | try
28 | {
29 | var noteCreatedRequest = new NoteCreatedRequest
30 | {
31 | Title = createNoteOptions.Title,
32 | Description = createNoteOptions.Body
33 | };
34 | var json = JsonSerializer.Serialize(noteCreatedRequest);
35 | var data = new StringContent(json, Encoding.UTF8, "application/json");
36 | var httpResponseMessage = await _httpClient.PostAsync("notes", data, ct);
37 |
38 | if (!httpResponseMessage.IsSuccessStatusCode)
39 | {
40 | var responseContentStr = await httpResponseMessage.Content.ReadAsStringAsync(ct);
41 |
42 | _logger.LogError("The HTTP request failed with the following error: {ResponseContent}", responseContentStr);
43 | }
44 | }
45 | catch (Exception ex)
46 | {
47 | _logger.LogError(ex, "Exception in {ClassName} -> {MethodName} method.", nameof(NotificationService), nameof(SendNoteCreatedEventAsync));
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/Services/NoteService.cs:
--------------------------------------------------------------------------------
1 | using Fta.DemoFunc.Api.Dtos;
2 | using Fta.DemoFunc.Api.Entities;
3 | using Fta.DemoFunc.Api.Interfaces;
4 | using Fta.DemoFunc.Api.Options;
5 | using Microsoft.Extensions.Logging;
6 | using System;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | namespace Fta.DemoFunc.Api.Services
11 | {
12 | public class NoteService : INoteService
13 | {
14 | private readonly INoteRepository _noteRepository;
15 | private readonly IDateTimeProvider _dateTimeProvider;
16 | private readonly INotificationService _notificationService;
17 | private readonly ILogger _logger;
18 |
19 | public NoteService(
20 | INoteRepository noteRepository,
21 | IDateTimeProvider dateTimeProvider,
22 | INotificationService notificationService,
23 | ILogger logger)
24 | {
25 | _noteRepository = noteRepository;
26 | _dateTimeProvider = dateTimeProvider;
27 | _notificationService = notificationService;
28 | _logger = logger;
29 | }
30 |
31 | public async Task CreateNoteAsync(CreateNoteOptions createNoteOptions, CancellationToken ct = default)
32 | {
33 | if (string.IsNullOrEmpty(createNoteOptions.Title) || string.IsNullOrEmpty(createNoteOptions.Body))
34 | {
35 | _logger.LogError("Title or body of note cannot be empty. Returning null by default.");
36 |
37 | return null;
38 | }
39 |
40 | var newNote = await _noteRepository.CreateAsync(new Note
41 | {
42 | Id = Guid.NewGuid(),
43 | Title = createNoteOptions.Title,
44 | Body = createNoteOptions.Body,
45 | CreatedAt = _dateTimeProvider.UtcNow,
46 | LastUpdatedOn = _dateTimeProvider.UtcNow
47 | }, ct);
48 |
49 | await _notificationService.SendNoteCreatedEventAsync(createNoteOptions, ct);
50 |
51 | return new NoteDto
52 | {
53 | Id = newNote.Id.ToString(),
54 | Title = newNote.Title,
55 | LastUpdatedOn = newNote.LastUpdatedOn,
56 | Body = newNote.Body
57 | };
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/NotesFunction.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Microsoft.AspNetCore.Mvc;
3 | using Microsoft.Azure.WebJobs;
4 | using Microsoft.Azure.WebJobs.Extensions.Http;
5 | using Fta.DemoFunc.Api.Interfaces;
6 | using System;
7 | using System.Web.Http;
8 | using Fta.DemoFunc.Api.Contracts.Requests;
9 | using System.Threading;
10 | using Fta.DemoFunc.Api.Options;
11 | using Fta.DemoFunc.Api.Contracts.Responses;
12 | using Microsoft.Extensions.Logging;
13 |
14 | namespace Fta.DemoFunc.Api
15 | {
16 | public class NotesFunction
17 | {
18 | private readonly INoteService _noteService;
19 | private readonly ILogger _logger;
20 |
21 | public NotesFunction(INoteService noteService, ILogger logger)
22 | {
23 | _noteService = noteService;
24 | _logger = logger;
25 | }
26 |
27 | [FunctionName("NotesFunction")]
28 | public async Task> Post(
29 | [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "notes")] CreateNoteRequest createNoteRequest, CancellationToken ct = default)
30 | {
31 | _logger.LogInformation("C# HTTP trigger NotesFunction processed a request.");
32 |
33 | try
34 | {
35 | var newNoteDto = await _noteService.CreateNoteAsync(new CreateNoteOptions
36 | {
37 | Body = createNoteRequest.Body,
38 | Title = createNoteRequest.Title
39 | }, ct);
40 |
41 | if (newNoteDto is null)
42 | {
43 | return new BadRequestObjectResult("This HTTP triggered NotesFunction executed successfully, but you passed in a bad request model for the note creation process.");
44 | }
45 |
46 | return new CreatedResult("/notes/" + newNoteDto.Id, new CreateNoteResponse
47 | {
48 | Id = newNoteDto.Id,
49 | Title = newNoteDto.Title,
50 | Body = newNoteDto.Body,
51 | LastUpdatedOn = newNoteDto.LastUpdatedOn
52 | });
53 | }
54 | catch (Exception ex)
55 | {
56 | _logger.LogError(ex, "Exception in {ClassName} -> {MethodName} method.", nameof(NotesFunction), nameof(Post));
57 |
58 | return new InternalServerErrorResult();
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/DependencyInjection.cs:
--------------------------------------------------------------------------------
1 | using Fta.DemoFunc.Api.Interfaces;
2 | using Fta.DemoFunc.Api.Services;
3 | using Microsoft.Extensions.Configuration;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using Polly.Extensions.Http;
6 | using Polly;
7 | using System;
8 | using System.Net.Http;
9 | using Fta.DemoFunc.Api.Persistence.Repositories;
10 | using Fta.DemoFunc.Api.Persistence;
11 |
12 | namespace Fta.DemoFunc.Api
13 | {
14 | public static class DependencyInjection
15 | {
16 | public static IServiceCollection AddApplication(this IServiceCollection services, IConfigurationRoot configuration)
17 | {
18 | services.AddScoped();
19 |
20 | return services;
21 | }
22 |
23 | public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
24 | {
25 | services.AddTransient();
26 |
27 | AddPersistence(services, configuration);
28 |
29 | services
30 | .AddHttpClient(client =>
31 | {
32 | client.BaseAddress = new Uri(configuration.GetValue("NotificationApiUrl"));
33 | })
34 | .AddPolicyHandler(GetRetryPolicy())
35 | .AddPolicyHandler(GetCircuitBreakerPolicy());
36 |
37 | return services;
38 | }
39 |
40 | private static void AddPersistence(IServiceCollection services, IConfiguration configuration)
41 | {
42 | services.AddSingleton(_ =>
43 | new NpgsqlConnectionFactory(configuration.GetConnectionString("Database")!));
44 | services.AddSingleton();
45 | services.AddSingleton();
46 | }
47 |
48 | private static IAsyncPolicy GetRetryPolicy()
49 | {
50 | return HttpPolicyExtensions
51 | .HandleTransientHttpError()
52 | .OrTransientHttpStatusCode()
53 | .WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
54 | }
55 |
56 | private static IAsyncPolicy GetCircuitBreakerPolicy()
57 | {
58 | return HttpPolicyExtensions
59 | .HandleTransientHttpError()
60 | .OrTransientHttpStatusCode()
61 | .CircuitBreakerAsync(3, TimeSpan.FromMinutes(2));
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/tests/Fta.DemoFunc.Api.Tests.Integration/TestHelpers.cs:
--------------------------------------------------------------------------------
1 | using Fta.DemoFunc.Api.Interfaces;
2 | using Fta.DemoFunc.Api.Persistence;
3 | using Fta.DemoFunc.Api.Services;
4 | using Microsoft.AspNetCore.Hosting;
5 | using Microsoft.Extensions.Configuration;
6 | using Microsoft.Extensions.DependencyInjection;
7 | using Microsoft.Extensions.DependencyInjection.Extensions;
8 | using Microsoft.Extensions.Hosting;
9 | using Microsoft.Extensions.Logging;
10 | using System;
11 | using System.Linq;
12 |
13 | namespace Fta.DemoFunc.Api.Tests.Integration
14 | {
15 | public static class TestHelpers
16 | {
17 | public static IHostBuilder ConfigureDefaultTestHost(this IHostBuilder builder, NotesFunctionFixture notesFunctionFixture)
18 | {
19 | return
20 | builder
21 | .ConfigureLogging(logging =>
22 | {
23 | logging.ClearProviders();
24 | })
25 | .ConfigureServices(services =>
26 | {
27 | var configuration = new ConfigurationBuilder()
28 | .SetBasePath(Environment.CurrentDirectory)
29 | .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
30 | .AddEnvironmentVariables()
31 | .Build();
32 |
33 | services.AddApplication(configuration);
34 | services.AddInfrastructure(configuration);
35 |
36 | services.RemoveAll(typeof(IHostedService));
37 |
38 | services.RemoveAll(typeof(IDbConnectionFactory));
39 | services.AddSingleton(_ =>
40 | new NpgsqlConnectionFactory(notesFunctionFixture.GetPostgresDbConnectionString()));
41 |
42 | var notificationServiceDescriptor = services.SingleOrDefault(
43 | d => d.ServiceType ==
44 | typeof(INotificationService));
45 |
46 | services.Remove(notificationServiceDescriptor!);
47 |
48 | services
49 | .AddHttpClient(client =>
50 | {
51 | client.BaseAddress = new Uri(notesFunctionFixture.GetNotificationApiServerUrl());
52 | });
53 |
54 | services.AddTransient();
55 | });
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/Fta.DemoFunc.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.4.33205.214
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4F311F7F-6DB7-4F73-B72A-3893A8496788}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{6C01E311-09E7-46AA-BE36-059C65A2EA83}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fta.DemoFunc.Api", "src\Fta.DemoFunc.Api\Fta.DemoFunc.Api.csproj", "{5126B44E-3E17-42D0-80CC-278DD8DB96A5}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fta.DemoFunc.Api.Tests.Unit", "tests\Fta.DemoFunc.Api.Tests.Unit\Fta.DemoFunc.Api.Tests.Unit.csproj", "{171D1C72-D240-402C-906B-3A9CA7A83B00}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fta.DemoFunc.Api.Tests.Integration", "tests\Fta.DemoFunc.Api.Tests.Integration\Fta.DemoFunc.Api.Tests.Integration.csproj", "{C1F0C7A1-4C73-4554-B2E2-40F19DB52328}"
15 | EndProject
16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{EBE815AC-9D0A-4E79-9432-984560C5AF74}"
17 | ProjectSection(SolutionItems) = preProject
18 | README.md = README.md
19 | EndProjectSection
20 | EndProject
21 | Global
22 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
23 | Debug|Any CPU = Debug|Any CPU
24 | Release|Any CPU = Release|Any CPU
25 | EndGlobalSection
26 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
27 | {5126B44E-3E17-42D0-80CC-278DD8DB96A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
28 | {5126B44E-3E17-42D0-80CC-278DD8DB96A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
29 | {5126B44E-3E17-42D0-80CC-278DD8DB96A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
30 | {5126B44E-3E17-42D0-80CC-278DD8DB96A5}.Release|Any CPU.Build.0 = Release|Any CPU
31 | {171D1C72-D240-402C-906B-3A9CA7A83B00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
32 | {171D1C72-D240-402C-906B-3A9CA7A83B00}.Debug|Any CPU.Build.0 = Debug|Any CPU
33 | {171D1C72-D240-402C-906B-3A9CA7A83B00}.Release|Any CPU.ActiveCfg = Release|Any CPU
34 | {171D1C72-D240-402C-906B-3A9CA7A83B00}.Release|Any CPU.Build.0 = Release|Any CPU
35 | {C1F0C7A1-4C73-4554-B2E2-40F19DB52328}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
36 | {C1F0C7A1-4C73-4554-B2E2-40F19DB52328}.Debug|Any CPU.Build.0 = Debug|Any CPU
37 | {C1F0C7A1-4C73-4554-B2E2-40F19DB52328}.Release|Any CPU.ActiveCfg = Release|Any CPU
38 | {C1F0C7A1-4C73-4554-B2E2-40F19DB52328}.Release|Any CPU.Build.0 = Release|Any CPU
39 | EndGlobalSection
40 | GlobalSection(SolutionProperties) = preSolution
41 | HideSolutionNode = FALSE
42 | EndGlobalSection
43 | GlobalSection(NestedProjects) = preSolution
44 | {5126B44E-3E17-42D0-80CC-278DD8DB96A5} = {4F311F7F-6DB7-4F73-B72A-3893A8496788}
45 | {171D1C72-D240-402C-906B-3A9CA7A83B00} = {6C01E311-09E7-46AA-BE36-059C65A2EA83}
46 | {C1F0C7A1-4C73-4554-B2E2-40F19DB52328} = {6C01E311-09E7-46AA-BE36-059C65A2EA83}
47 | EndGlobalSection
48 | GlobalSection(ExtensibilityGlobals) = postSolution
49 | SolutionGuid = {F410718E-26E7-4467-9A99-BFF68FC96207}
50 | EndGlobalSection
51 | EndGlobal
52 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Azure Functions in-process model code testing sample
2 |
3 | Unit and integration tests examples for **Azure Functions in-process model**.
4 |
5 | This repo is a sample .NET 6 "Create Notes" API written using Azure Functions template and contains examples on how to properly write unit and integration tests for the Azure Functions classes.
6 |
7 | ## Features
8 |
9 | This project framework provides the following features:
10 |
11 | * **Unit tests** for Azure Function classes
12 | * **Integration tests** for Azure Function classes
13 |
14 | ## Getting Started
15 |
16 | ### Prerequisites
17 |
18 | The code in this repo requires knowledge of the following concepts and frameworks:
19 |
20 | - **Unit testing**
21 | - **Integration testing**
22 | - [**xUnit**](https://github.com/xunit/xunit)
23 | - Testing framework
24 | - [**NSubstitute**](https://nsubstitute.github.io/)
25 | - Mocking framework
26 | - [**Bogus**](https://github.com/bchavez/Bogus)
27 | - Fake data generator for C#
28 | - [**FluentAssertions**](https://fluentassertions.com/)
29 | - [**Test Containers**](https://testcontainers.com/)
30 | - [**WireMock.Net**](https://github.com/WireMock-Net/WireMock.Net)
31 |
32 | You also need to have **Docker Desktop** installed in your local machine.
33 |
34 | ### Installation
35 |
36 | The API uses **Postgresql** database management system to store the created Notes.
37 |
38 | To run an instance locally with docker you must run your API against a local instance of **Postgresql**, by simply typing the following command:
39 |
40 | ```
41 | docker run --name postgresql -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 -v /data:/var/lib/postgresql/data -d postgres
42 | ```
43 |
44 | To access the Postgresql instance through a management interface you can also run the following docker container:
45 |
46 | ```
47 | docker run --name my-pgadmin -p 82:80 -e 'PGADMIN_DEFAULT_EMAIL=' -e 'PGADMIN_DEFAULT_PASSWORD=root'-d dpage/pgadmin4
48 | ```
49 |
50 | And then access it via http://localhost:82 from a browser in your local machine.
51 |
52 | ### Quickstart
53 |
54 | To be able to run this demo on your own in your local machine you need to create a **local.settings.json** file inside the **Fta.DemoFunc.Api** project.
55 |
56 | As it’s not advisable to store keys and secrets inside a git repository, for local development you can use a local.settings.json file to store configuration.
57 |
58 | Sample **local.settings.json** file:
59 |
60 | ```
61 | {
62 | "IsEncrypted": false,
63 | "Values": {
64 | "AzureWebJobsStorage": "UseDevelopmentStorage=true",
65 | "FUNCTIONS_WORKER_RUNTIME": "dotnet"
66 | },
67 | "NotificationApiUrl": "https://63cacb2d4f53a004202b1df7.mockapi.io/api/v1/",
68 | "ConnectionStrings": {
69 | "Database": "Server=localhost;Port=5432;Database=notesdb;User ID=postgres;Password=postgres;"
70 | }
71 | }
72 | ```
73 |
74 | The "NotificationApiUrl" setting is a mock 3rd party API (created with [mockapi.io](https://mockapi.io/)) which we call to send notification events that a new note has been created into our system.
75 |
76 | You can use the "NotificationApiUrl" setting as is to run the demo API locally. It is here to demonstrate how to handle integration tests against 3rd party APIs that you do not control.
--------------------------------------------------------------------------------
/tests/Fta.DemoFunc.Api.Tests.Integration/NotesFunctionTests.cs:
--------------------------------------------------------------------------------
1 | using Bogus;
2 | using FluentAssertions;
3 | using Fta.DemoFunc.Api.Contracts.Requests;
4 | using Fta.DemoFunc.Api.Contracts.Responses;
5 | using Microsoft.AspNetCore.Http;
6 | using Microsoft.AspNetCore.Mvc;
7 | using Microsoft.Azure.WebJobs;
8 | using Microsoft.Extensions.DependencyInjection;
9 | using Microsoft.Extensions.Hosting;
10 | using Xunit;
11 |
12 | namespace Fta.DemoFunc.Api.Tests.Integration
13 | {
14 | public class NotesFunctionTests : IClassFixture
15 | {
16 | private readonly NotesFunctionFixture _notesFunctionFixture;
17 | private readonly Faker _noteGenerator =
18 | new Faker()
19 | .RuleFor(x => x.Title, faker => faker.Lorem.Random.Words())
20 | .RuleFor(x => x.Body, faker => faker.Lorem.Random.Words());
21 |
22 | public NotesFunctionTests(NotesFunctionFixture notesFunctionFixture)
23 | {
24 | _notesFunctionFixture = notesFunctionFixture;
25 | }
26 |
27 | [Fact]
28 | public async void Post_ShouldCreateNote_WhenCalledWithValidNoteDetails()
29 | {
30 | var host = new HostBuilder()
31 | .ConfigureWebJobs(builder => builder.UseExternalStartup())
32 | .ConfigureDefaultTestHost(_notesFunctionFixture)
33 | .Build();
34 |
35 | using (host)
36 | {
37 | await host.StartAsync();
38 |
39 | var sut = host.Services.GetRequiredService();
40 |
41 | // Arrange
42 | var createValidNoteRequest = _noteGenerator.Generate();
43 |
44 | // Act
45 | var response = await sut.Post(createValidNoteRequest);
46 |
47 | // Assert
48 | var createdResult = (CreatedResult)response.Result!;
49 | var createNoteResponse = createdResult.Value as CreateNoteResponse;
50 | createdResult.StatusCode.Should().Be(StatusCodes.Status201Created);
51 | createNoteResponse!.Title.Should().Be(createValidNoteRequest.Title);
52 | createNoteResponse.Body.Should().Be(createValidNoteRequest.Body);
53 | }
54 | }
55 |
56 | [Fact]
57 | public async void Post_ShouldReturnBadRequest_WhenCalledWithInvalidNoteDetails()
58 | {
59 | var host = new HostBuilder()
60 | .ConfigureWebJobs(builder => builder.UseExternalStartup())
61 | .ConfigureDefaultTestHost(_notesFunctionFixture)
62 | .Build();
63 |
64 | using (host)
65 | {
66 | await host.StartAsync();
67 |
68 | var sut = host.Services.GetRequiredService();
69 |
70 | // Arrange
71 | var createInvalidNoteRequest = new CreateNoteRequest();
72 |
73 | // Act
74 | var response = await sut.Post(createInvalidNoteRequest);
75 |
76 | // Assert
77 | var badRequestObjectResult = (BadRequestObjectResult)response.Result!;
78 | badRequestObjectResult.Should().NotBeNull();
79 | badRequestObjectResult.StatusCode.Should().Be(StatusCodes.Status400BadRequest);
80 | badRequestObjectResult.Value.Should().Be("This HTTP triggered NotesFunction executed successfully, but you passed in a bad request model for the note creation process.");
81 | }
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to [project-title]
2 |
3 | This project welcomes contributions and suggestions. Most contributions require you to agree to a
4 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
5 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
6 |
7 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide
8 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
9 | provided by the bot. You will only need to do this once across all repos using our CLA.
10 |
11 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
12 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
13 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
14 |
15 | - [Code of Conduct](#coc)
16 | - [Issues and Bugs](#issue)
17 | - [Feature Requests](#feature)
18 | - [Submission Guidelines](#submit)
19 |
20 | ## Code of Conduct
21 | Help us keep this project open and inclusive. Please read and follow our [Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
22 |
23 | ## Found an Issue?
24 | If you find a bug in the source code or a mistake in the documentation, you can help us by
25 | [submitting an issue](#submit-issue) to the GitHub Repository. Even better, you can
26 | [submit a Pull Request](#submit-pr) with a fix.
27 |
28 | ## Want a Feature?
29 | You can *request* a new feature by [submitting an issue](#submit-issue) to the GitHub
30 | Repository. If you would like to *implement* a new feature, please submit an issue with
31 | a proposal for your work first, to be sure that we can use it.
32 |
33 | * **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
34 |
35 | ## Submission Guidelines
36 |
37 | ### Submitting an Issue
38 | Before you submit an issue, search the archive, maybe your question was already answered.
39 |
40 | If your issue appears to be a bug, and hasn't been reported, open a new issue.
41 | Help us to maximize the effort we can spend fixing issues and adding new
42 | features, by not reporting duplicate issues. Providing the following information will increase the
43 | chances of your issue being dealt with quickly:
44 |
45 | * **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
46 | * **Version** - what version is affected (e.g. 0.1.2)
47 | * **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you
48 | * **Browsers and Operating System** - is this a problem with all browsers?
49 | * **Reproduce the Error** - provide a live example or a unambiguous set of steps
50 | * **Related Issues** - has a similar issue been reported before?
51 | * **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
52 | causing the problem (line of code or commit)
53 |
54 | You can file new issues by providing the above information at the corresponding repository's issues link: https://github.com/[organization-name]/[repository-name]/issues/new].
55 |
56 | ### Submitting a Pull Request (PR)
57 | Before you submit your Pull Request (PR) consider the following guidelines:
58 |
59 | * Search the repository (https://github.com/[organization-name]/[repository-name]/pulls) for an open or closed PR
60 | that relates to your submission. You don't want to duplicate effort.
61 |
62 | * Make your changes in a new git fork:
63 |
64 | * Commit your changes using a descriptive commit message
65 | * Push your fork to GitHub:
66 | * In GitHub, create a pull request
67 | * If we suggest changes then:
68 | * Make the required updates.
69 | * Rebase your fork and force push to your GitHub repository (this will update your Pull Request):
70 |
71 | ```shell
72 | git rebase master -i
73 | git push -f
74 | ```
75 |
76 | That's it! Thank you for your contribution!
77 |
--------------------------------------------------------------------------------
/tests/Fta.DemoFunc.Api.Tests.Unit/NotesFunctionTests.cs:
--------------------------------------------------------------------------------
1 | using Fta.DemoFunc.Api.Interfaces;
2 | using Microsoft.AspNetCore.Http;
3 | using Microsoft.AspNetCore.Mvc;
4 | using NSubstitute;
5 | using FluentAssertions;
6 | using NSubstitute.ReturnsExtensions;
7 | using System.Web.Http;
8 | using NSubstitute.ExceptionExtensions;
9 | using Fta.DemoFunc.Api.Options;
10 | using Fta.DemoFunc.Api.Dtos;
11 | using Fta.DemoFunc.Api.Contracts.Requests;
12 | using Fta.DemoFunc.Api.Contracts.Responses;
13 | using System;
14 | using Xunit;
15 | using System.Threading.Tasks;
16 | using System.Threading;
17 | using Microsoft.Extensions.Logging;
18 | using Microsoft.Extensions.Logging.Abstractions;
19 | using Bogus;
20 |
21 | namespace Fta.DemoFunc.Api.Tests.Unit
22 | {
23 | public class NotesFunctionTests
24 | {
25 | private readonly NotesFunction _sut;
26 |
27 | private readonly string _newNoteId = Guid.NewGuid().ToString();
28 | private readonly DateTime _newNoteLastUpdatedOn = DateTime.UtcNow;
29 | private readonly INoteService _noteService = Substitute.For();
30 | private readonly ILogger _logger = NullLogger.Instance;
31 | private readonly Faker _noteGenerator =
32 | new Faker()
33 | .RuleFor(x => x.Title, faker => faker.Lorem.Random.Words())
34 | .RuleFor(x => x.Body, faker => faker.Lorem.Random.Words());
35 |
36 | public NotesFunctionTests()
37 | {
38 | _sut = new NotesFunction(_noteService, _logger);
39 | }
40 |
41 | [Fact]
42 | public async Task Post_ShouldReturnOkObjectResultWithCreatedNoteDetails_WhenCalledWithValidNoteDetails()
43 | {
44 | // Arrange
45 | var createNoteRequest = _noteGenerator.Generate();
46 |
47 | var expectedResult = new CreateNoteResponse
48 | {
49 | Id = _newNoteId,
50 | LastUpdatedOn = _newNoteLastUpdatedOn,
51 | Title = createNoteRequest.Title,
52 | Body = createNoteRequest.Body
53 | };
54 |
55 | _noteService.CreateNoteAsync(Arg.Any(), Arg.Any()).Returns(new NoteDto
56 | {
57 | Id = _newNoteId,
58 | Body = createNoteRequest.Body,
59 | Title = createNoteRequest.Title,
60 | LastUpdatedOn = _newNoteLastUpdatedOn
61 | });
62 |
63 | // Act
64 | var response = await _sut.Post(createNoteRequest);
65 |
66 | // Assert
67 | var result = response.Result as CreatedResult;
68 | result!.StatusCode.Should().Be(StatusCodes.Status201Created);
69 | result!.Value.Should().BeEquivalentTo(expectedResult);
70 | }
71 |
72 | [Fact]
73 | public async Task Post_ShouldReturnBadRequestObjectResultWithRespectiveErrorMessage_WhenCalledWithInvalidNoteDetails()
74 | {
75 | // Arrange
76 | _noteService.CreateNoteAsync(Arg.Any(), Arg.Any()).ReturnsNull();
77 |
78 | // Act
79 | var response = await _sut.Post(_noteGenerator.Generate());
80 |
81 | // Assert
82 | var result = response.Result as BadRequestObjectResult;
83 | result!.StatusCode.Should().Be(StatusCodes.Status400BadRequest);
84 | result!.Value.Should().BeEquivalentTo("This HTTP triggered NotesFunction executed successfully, but you passed in a bad request model for the note creation process.");
85 | }
86 |
87 | [Fact]
88 | public async Task Post_ShouldLogTheExceptionAndReturnInternalServerError_WhenNoteServiceThrowsAnException()
89 | {
90 | // Arrange
91 | _noteService.CreateNoteAsync(Arg.Any(), Arg.Any()).ThrowsAsync();
92 |
93 | // Act
94 | var response = await _sut.Post(_noteGenerator.Generate());
95 |
96 | // Assert
97 | var result = response.Result as InternalServerErrorResult;
98 | result!.StatusCode.Should().Be(StatusCodes.Status500InternalServerError);
99 | }
100 | }
101 | }
--------------------------------------------------------------------------------
/src/Fta.DemoFunc.Api/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # Azure Functions localsettings file
5 | local.settings.json
6 |
7 | # User-specific files
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | bld/
24 | [Bb]in/
25 | [Oo]bj/
26 | [Ll]og/
27 |
28 | # Visual Studio 2015 cache/options directory
29 | .vs/
30 | # Uncomment if you have tasks that create the project's static files in wwwroot
31 | #wwwroot/
32 |
33 | # MSTest test Results
34 | [Tt]est[Rr]esult*/
35 | [Bb]uild[Ll]og.*
36 |
37 | # NUNIT
38 | *.VisualState.xml
39 | TestResult.xml
40 |
41 | # Build Results of an ATL Project
42 | [Dd]ebugPS/
43 | [Rr]eleasePS/
44 | dlldata.c
45 |
46 | # DNX
47 | project.lock.json
48 | project.fragment.lock.json
49 | artifacts/
50 |
51 | *_i.c
52 | *_p.c
53 | *_i.h
54 | *.ilk
55 | *.meta
56 | *.obj
57 | *.pch
58 | *.pdb
59 | *.pgc
60 | *.pgd
61 | *.rsp
62 | *.sbr
63 | *.tlb
64 | *.tli
65 | *.tlh
66 | *.tmp
67 | *.tmp_proj
68 | *.log
69 | *.vspscc
70 | *.vssscc
71 | .builds
72 | *.pidb
73 | *.svclog
74 | *.scc
75 |
76 | # Chutzpah Test files
77 | _Chutzpah*
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opendb
84 | *.opensdf
85 | *.sdf
86 | *.cachefile
87 | *.VC.db
88 | *.VC.VC.opendb
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 | *.sap
95 |
96 | # TFS 2012 Local Workspace
97 | $tf/
98 |
99 | # Guidance Automation Toolkit
100 | *.gpState
101 |
102 | # ReSharper is a .NET coding add-in
103 | _ReSharper*/
104 | *.[Rr]e[Ss]harper
105 | *.DotSettings.user
106 |
107 | # JustCode is a .NET coding add-in
108 | .JustCode
109 |
110 | # TeamCity is a build add-in
111 | _TeamCity*
112 |
113 | # DotCover is a Code Coverage Tool
114 | *.dotCover
115 |
116 | # NCrunch
117 | _NCrunch_*
118 | .*crunch*.local.xml
119 | nCrunchTemp_*
120 |
121 | # MightyMoose
122 | *.mm.*
123 | AutoTest.Net/
124 |
125 | # Web workbench (sass)
126 | .sass-cache/
127 |
128 | # Installshield output folder
129 | [Ee]xpress/
130 |
131 | # DocProject is a documentation generator add-in
132 | DocProject/buildhelp/
133 | DocProject/Help/*.HxT
134 | DocProject/Help/*.HxC
135 | DocProject/Help/*.hhc
136 | DocProject/Help/*.hhk
137 | DocProject/Help/*.hhp
138 | DocProject/Help/Html2
139 | DocProject/Help/html
140 |
141 | # Click-Once directory
142 | publish/
143 |
144 | # Publish Web Output
145 | *.[Pp]ublish.xml
146 | *.azurePubxml
147 | # TODO: Comment the next line if you want to checkin your web deploy settings
148 | # but database connection strings (with potential passwords) will be unencrypted
149 | #*.pubxml
150 | *.publishproj
151 |
152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
153 | # checkin your Azure Web App publish settings, but sensitive information contained
154 | # in these scripts will be unencrypted
155 | PublishScripts/
156 |
157 | # NuGet Packages
158 | *.nupkg
159 | # The packages folder can be ignored because of Package Restore
160 | **/packages/*
161 | # except build/, which is used as an MSBuild target.
162 | !**/packages/build/
163 | # Uncomment if necessary however generally it will be regenerated when needed
164 | #!**/packages/repositories.config
165 | # NuGet v3's project.json files produces more ignoreable files
166 | *.nuget.props
167 | *.nuget.targets
168 |
169 | # Microsoft Azure Build Output
170 | csx/
171 | *.build.csdef
172 |
173 | # Microsoft Azure Emulator
174 | ecf/
175 | rcf/
176 |
177 | # Windows Store app package directories and files
178 | AppPackages/
179 | BundleArtifacts/
180 | Package.StoreAssociation.xml
181 | _pkginfo.txt
182 |
183 | # Visual Studio cache files
184 | # files ending in .cache can be ignored
185 | *.[Cc]ache
186 | # but keep track of directories ending in .cache
187 | !*.[Cc]ache/
188 |
189 | # Others
190 | ClientBin/
191 | ~$*
192 | *~
193 | *.dbmdl
194 | *.dbproj.schemaview
195 | *.jfm
196 | *.pfx
197 | *.publishsettings
198 | node_modules/
199 | orleans.codegen.cs
200 |
201 | # Since there are multiple workflows, uncomment next line to ignore bower_components
202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
203 | #bower_components/
204 |
205 | # RIA/Silverlight projects
206 | Generated_Code/
207 |
208 | # Backup & report files from converting an old project file
209 | # to a newer Visual Studio version. Backup files are not needed,
210 | # because we have git ;-)
211 | _UpgradeReport_Files/
212 | Backup*/
213 | UpgradeLog*.XML
214 | UpgradeLog*.htm
215 |
216 | # SQL Server files
217 | *.mdf
218 | *.ldf
219 |
220 | # Business Intelligence projects
221 | *.rdl.data
222 | *.bim.layout
223 | *.bim_*.settings
224 |
225 | # Microsoft Fakes
226 | FakesAssemblies/
227 |
228 | # GhostDoc plugin setting file
229 | *.GhostDoc.xml
230 |
231 | # Node.js Tools for Visual Studio
232 | .ntvs_analysis.dat
233 |
234 | # Visual Studio 6 build log
235 | *.plg
236 |
237 | # Visual Studio 6 workspace options file
238 | *.opt
239 |
240 | # Visual Studio LightSwitch build output
241 | **/*.HTMLClient/GeneratedArtifacts
242 | **/*.DesktopClient/GeneratedArtifacts
243 | **/*.DesktopClient/ModelManifest.xml
244 | **/*.Server/GeneratedArtifacts
245 | **/*.Server/ModelManifest.xml
246 | _Pvt_Extensions
247 |
248 | # Paket dependency manager
249 | .paket/paket.exe
250 | paket-files/
251 |
252 | # FAKE - F# Make
253 | .fake/
254 |
255 | # JetBrains Rider
256 | .idea/
257 | *.sln.iml
258 |
259 | # CodeRush
260 | .cr/
261 |
262 | # Python Tools for Visual Studio (PTVS)
263 | __pycache__/
264 | *.pyc
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Oo]ut/
33 | [Ll]og/
34 | [Ll]ogs/
35 |
36 | # Visual Studio 2015/2017 cache/options directory
37 | .vs/
38 | # Uncomment if you have tasks that create the project's static files in wwwroot
39 | #wwwroot/
40 |
41 | # Visual Studio 2017 auto generated files
42 | Generated\ Files/
43 |
44 | # MSTest test Results
45 | [Tt]est[Rr]esult*/
46 | [Bb]uild[Ll]og.*
47 |
48 | # NUnit
49 | *.VisualState.xml
50 | TestResult.xml
51 | nunit-*.xml
52 |
53 | # Build Results of an ATL Project
54 | [Dd]ebugPS/
55 | [Rr]eleasePS/
56 | dlldata.c
57 |
58 | # Benchmark Results
59 | BenchmarkDotNet.Artifacts/
60 |
61 | # .NET Core
62 | project.lock.json
63 | project.fragment.lock.json
64 | artifacts/
65 |
66 | # ASP.NET Scaffolding
67 | ScaffoldingReadMe.txt
68 |
69 | # StyleCop
70 | StyleCopReport.xml
71 |
72 | # Files built by Visual Studio
73 | *_i.c
74 | *_p.c
75 | *_h.h
76 | *.ilk
77 | *.meta
78 | *.obj
79 | *.iobj
80 | *.pch
81 | *.pdb
82 | *.ipdb
83 | *.pgc
84 | *.pgd
85 | *.rsp
86 | *.sbr
87 | *.tlb
88 | *.tli
89 | *.tlh
90 | *.tmp
91 | *.tmp_proj
92 | *_wpftmp.csproj
93 | *.log
94 | *.vspscc
95 | *.vssscc
96 | .builds
97 | *.pidb
98 | *.svclog
99 | *.scc
100 |
101 | # Chutzpah Test files
102 | _Chutzpah*
103 |
104 | # Visual C++ cache files
105 | ipch/
106 | *.aps
107 | *.ncb
108 | *.opendb
109 | *.opensdf
110 | *.sdf
111 | *.cachefile
112 | *.VC.db
113 | *.VC.VC.opendb
114 |
115 | # Visual Studio profiler
116 | *.psess
117 | *.vsp
118 | *.vspx
119 | *.sap
120 |
121 | # Visual Studio Trace Files
122 | *.e2e
123 |
124 | # TFS 2012 Local Workspace
125 | $tf/
126 |
127 | # Guidance Automation Toolkit
128 | *.gpState
129 |
130 | # ReSharper is a .NET coding add-in
131 | _ReSharper*/
132 | *.[Rr]e[Ss]harper
133 | *.DotSettings.user
134 |
135 | # TeamCity is a build add-in
136 | _TeamCity*
137 |
138 | # DotCover is a Code Coverage Tool
139 | *.dotCover
140 |
141 | # AxoCover is a Code Coverage Tool
142 | .axoCover/*
143 | !.axoCover/settings.json
144 |
145 | # Coverlet is a free, cross platform Code Coverage Tool
146 | coverage*.json
147 | coverage*.xml
148 | coverage*.info
149 |
150 | # Visual Studio code coverage results
151 | *.coverage
152 | *.coveragexml
153 |
154 | # NCrunch
155 | _NCrunch_*
156 | .*crunch*.local.xml
157 | nCrunchTemp_*
158 |
159 | # MightyMoose
160 | *.mm.*
161 | AutoTest.Net/
162 |
163 | # Web workbench (sass)
164 | .sass-cache/
165 |
166 | # Installshield output folder
167 | [Ee]xpress/
168 |
169 | # DocProject is a documentation generator add-in
170 | DocProject/buildhelp/
171 | DocProject/Help/*.HxT
172 | DocProject/Help/*.HxC
173 | DocProject/Help/*.hhc
174 | DocProject/Help/*.hhk
175 | DocProject/Help/*.hhp
176 | DocProject/Help/Html2
177 | DocProject/Help/html
178 |
179 | # Click-Once directory
180 | publish/
181 |
182 | # Publish Web Output
183 | *.[Pp]ublish.xml
184 | *.azurePubxml
185 | # Note: Comment the next line if you want to checkin your web deploy settings,
186 | # but database connection strings (with potential passwords) will be unencrypted
187 | *.pubxml
188 | *.publishproj
189 |
190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
191 | # checkin your Azure Web App publish settings, but sensitive information contained
192 | # in these scripts will be unencrypted
193 | PublishScripts/
194 |
195 | # NuGet Packages
196 | *.nupkg
197 | # NuGet Symbol Packages
198 | *.snupkg
199 | # The packages folder can be ignored because of Package Restore
200 | **/[Pp]ackages/*
201 | # except build/, which is used as an MSBuild target.
202 | !**/[Pp]ackages/build/
203 | # Uncomment if necessary however generally it will be regenerated when needed
204 | #!**/[Pp]ackages/repositories.config
205 | # NuGet v3's project.json files produces more ignorable files
206 | *.nuget.props
207 | *.nuget.targets
208 |
209 | # Microsoft Azure Build Output
210 | csx/
211 | *.build.csdef
212 |
213 | # Microsoft Azure Emulator
214 | ecf/
215 | rcf/
216 |
217 | # Windows Store app package directories and files
218 | AppPackages/
219 | BundleArtifacts/
220 | Package.StoreAssociation.xml
221 | _pkginfo.txt
222 | *.appx
223 | *.appxbundle
224 | *.appxupload
225 |
226 | # Visual Studio cache files
227 | # files ending in .cache can be ignored
228 | *.[Cc]ache
229 | # but keep track of directories ending in .cache
230 | !?*.[Cc]ache/
231 |
232 | # Others
233 | ClientBin/
234 | ~$*
235 | *~
236 | *.dbmdl
237 | *.dbproj.schemaview
238 | *.jfm
239 | *.pfx
240 | *.publishsettings
241 | orleans.codegen.cs
242 |
243 | # Including strong name files can present a security risk
244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
245 | #*.snk
246 |
247 | # Since there are multiple workflows, uncomment next line to ignore bower_components
248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
249 | #bower_components/
250 |
251 | # RIA/Silverlight projects
252 | Generated_Code/
253 |
254 | # Backup & report files from converting an old project file
255 | # to a newer Visual Studio version. Backup files are not needed,
256 | # because we have git ;-)
257 | _UpgradeReport_Files/
258 | Backup*/
259 | UpgradeLog*.XML
260 | UpgradeLog*.htm
261 | ServiceFabricBackup/
262 | *.rptproj.bak
263 |
264 | # SQL Server files
265 | *.mdf
266 | *.ldf
267 | *.ndf
268 |
269 | # Business Intelligence projects
270 | *.rdl.data
271 | *.bim.layout
272 | *.bim_*.settings
273 | *.rptproj.rsuser
274 | *- [Bb]ackup.rdl
275 | *- [Bb]ackup ([0-9]).rdl
276 | *- [Bb]ackup ([0-9][0-9]).rdl
277 |
278 | # Microsoft Fakes
279 | FakesAssemblies/
280 |
281 | # GhostDoc plugin setting file
282 | *.GhostDoc.xml
283 |
284 | # Node.js Tools for Visual Studio
285 | .ntvs_analysis.dat
286 | node_modules/
287 |
288 | # Visual Studio 6 build log
289 | *.plg
290 |
291 | # Visual Studio 6 workspace options file
292 | *.opt
293 |
294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
295 | *.vbw
296 |
297 | # Visual Studio LightSwitch build output
298 | **/*.HTMLClient/GeneratedArtifacts
299 | **/*.DesktopClient/GeneratedArtifacts
300 | **/*.DesktopClient/ModelManifest.xml
301 | **/*.Server/GeneratedArtifacts
302 | **/*.Server/ModelManifest.xml
303 | _Pvt_Extensions
304 |
305 | # Paket dependency manager
306 | .paket/paket.exe
307 | paket-files/
308 |
309 | # FAKE - F# Make
310 | .fake/
311 |
312 | # CodeRush personal settings
313 | .cr/personal
314 |
315 | # Python Tools for Visual Studio (PTVS)
316 | __pycache__/
317 | *.pyc
318 |
319 | # Cake - Uncomment if you are using it
320 | # tools/**
321 | # !tools/packages.config
322 |
323 | # Tabs Studio
324 | *.tss
325 |
326 | # Telerik's JustMock configuration file
327 | *.jmconfig
328 |
329 | # BizTalk build output
330 | *.btp.cs
331 | *.btm.cs
332 | *.odx.cs
333 | *.xsd.cs
334 |
335 | # OpenCover UI analysis results
336 | OpenCover/
337 |
338 | # Azure Stream Analytics local run output
339 | ASALocalRun/
340 |
341 | # MSBuild Binary and Structured Log
342 | *.binlog
343 |
344 | # NVidia Nsight GPU debugger configuration file
345 | *.nvuser
346 |
347 | # MFractors (Xamarin productivity tool) working folder
348 | .mfractor/
349 |
350 | # Local History for Visual Studio
351 | .localhistory/
352 |
353 | # BeatPulse healthcheck temp database
354 | healthchecksdb
355 |
356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
357 | MigrationBackup/
358 |
359 | # Ionide (cross platform F# VS Code tools) working folder
360 | .ionide/
361 |
362 | # Fody - auto-generated XML schema
363 | FodyWeavers.xsd
--------------------------------------------------------------------------------