initial commit

This commit is contained in:
Julian
2022-01-23 19:20:13 +01:00
parent d4508520d4
commit 0e1b585db6
25 changed files with 758 additions and 0 deletions

24
.dockerignore Normal file
View File

@@ -0,0 +1,24 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/bin
**/charts
**/docker-compose*
**/compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
README.md

39
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,39 @@
{
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/net5.0/weddingapi.dll",
"args": [],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
},
{
"name": "Docker .NET Core Launch",
"type": "docker",
"request": "launch",
"preLaunchTask": "docker-run: debug",
"netCore": {
"appProject": "${workspaceFolder}/weddingapi.csproj"
}
}
]
}

98
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,98 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/weddingapi.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/weddingapi.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/weddingapi.csproj"
],
"problemMatcher": "$msCompile"
},
{
"type": "docker-build",
"label": "docker-build: debug",
"dependsOn": [
"build"
],
"dockerBuild": {
"tag": "weddingapi:dev",
"target": "base",
"dockerfile": "${workspaceFolder}/Dockerfile",
"context": "${workspaceFolder}",
"pull": true
},
"netCore": {
"appProject": "${workspaceFolder}/weddingapi.csproj"
}
},
{
"type": "docker-build",
"label": "docker-build: release",
"dependsOn": [
"build"
],
"dockerBuild": {
"tag": "weddingapi:latest",
"dockerfile": "${workspaceFolder}/Dockerfile",
"context": "${workspaceFolder}",
"pull": true
},
"netCore": {
"appProject": "${workspaceFolder}/weddingapi.csproj"
}
},
{
"type": "docker-run",
"label": "docker-run: debug",
"dependsOn": [
"docker-build: debug"
],
"dockerRun": {},
"netCore": {
"appProject": "${workspaceFolder}/weddingapi.csproj",
"enableDebugging": true,
"configureSsl": false
}
},
{
"type": "docker-run",
"label": "docker-run: release",
"dependsOn": [
"docker-build: release"
],
"dockerRun": {},
"netCore": {
"appProject": "${workspaceFolder}/weddingapi.csproj"
}
}
]
}

15
Config/MongoDbConfig.cs Normal file
View File

@@ -0,0 +1,15 @@
namespace weddingapi.Config
{
class MongoDbConfig
{
public string Host { get; set; }
public int Port { get; set; }
public string ConnectionString {
get
{
return $"mongodb://{Host}:{Port}";
}
}
}
}

View File

@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using weddingapi.Models;
using weddingapi.Repositories;
using weddingapi.Dtos;
using weddingapi.Converters;
using System.Threading.Tasks;
namespace weddingapi.Controllers
{
[ApiController]
[Route("family")]
public class FamilyController : ControllerBase
{
private readonly IFamilyRepository repository;
public FamilyController(IFamilyRepository repository)
{
this.repository = repository;
}
// GET /families
[HttpGet]
public async Task<IEnumerable<FamilyDto>> GetFamiliesAsync()
{
return (await repository.GetFamiliesAsync()).Select(family => family.AsDto());
}
// GET /family/{id}
[HttpGet("{id}")]
public async Task<ActionResult<FamilyDto>> GetFamilyAsync(Guid id)
{
var family = await repository.GetFamilyAsync(id);
if(family is null)
{
return NotFound();
}
return family.AsDto();
}
// POST /family
[HttpPost]
public async Task<ActionResult<FamilyDto>> CreateFamilyAsync(CreateFamilyDto familyDto)
{
Family fam = new Family()
{
Id = Guid.NewGuid(),
CreatedDate = DateTimeOffset.UtcNow,
Name = familyDto.Name,
Members = familyDto.Members.Select(person => new Person() {
IsChild = person.IsChild,
Name = person.Name,
}).ToList(),
};
await repository.CreateFamilyAsync(fam);
return CreatedAtAction(nameof(GetFamilyAsync), new { id = fam.Id }, fam.AsDto());
}
// PUT /family/{id}
[HttpPut("{id}")]
public async Task<ActionResult> UpdateFamilyAsync(Guid id, UpdateFamilyDto familyDto)
{
var exisitingFamily = await repository.GetFamilyAsync(id);
if(exisitingFamily is null)
{
return NotFound();
}
Family updatedFamily = exisitingFamily with
{
Members = familyDto.Members.Select(person => new Person() {
Name = person.Name,
IsChild = person.IsChild,
}).ToList(),
Name = familyDto.Name,
};
await repository.UpdateFamilyAsync(updatedFamily);
return NoContent();
}
// Delete /family/{id}
[HttpDelete("{id}")]
public async Task<ActionResult> DeleteFamilyAsync(Guid id)
{
var exisitingFamily = repository.GetFamilyAsync(id);
if(exisitingFamily is null)
{
return NotFound();
}
await repository.DeleteFamilyAsync(id);
return NoContent();
}
}
}

30
Converters/Extensions.cs Normal file
View File

@@ -0,0 +1,30 @@
using System.Linq;
using weddingapi.Dtos;
using weddingapi.Models;
namespace weddingapi.Converters
{
public static class Converters
{
public static FamilyDto AsDto(this Family family)
{
return new FamilyDto()
{
Id = family.Id,
CreatedDate = family.CreatedDate,
Members = family.Members.Select(person => person.AsDto()).ToList(),
Name = family.Name,
};
}
public static PersonDto AsDto(this Person person)
{
return new PersonDto()
{
IsChild = person.IsChild,
Name = person.Name,
};
}
}
}

17
Dockerfile Normal file
View File

@@ -0,0 +1,17 @@
FROM mcr.microsoft.com/dotnet/aspnet:5.0-focal AS base
WORKDIR /app
EXPOSE 80
ENV ASPNETCORE_URLS=http://+:80
FROM mcr.microsoft.com/dotnet/sdk:5.0-focal AS build
WORKDIR /src
COPY ["weddingapi.csproj", "./"]
RUN dotnet restore "weddingapi.csproj"
COPY . .
RUN dotnet publish "weddingapi.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "weddingapi.dll"]

13
Dtos/CreateFamilyDto.cs Normal file
View File

@@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace weddingapi.Dtos
{
public record CreateFamilyDto
{
[Required]
public string Name { get; init; }
[Required]
public IList<PersonDto> Members { get; init;}
}
}

16
Dtos/FamilyDto.cs Normal file
View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
namespace weddingapi.Dtos
{
public record FamilyDto
{
public Guid Id { get; init; }
public string Name { get; init; }
public DateTimeOffset CreatedDate { get; set; }
public IList<PersonDto> Members { get; init;}
}
}

11
Dtos/PersonDto.cs Normal file
View File

@@ -0,0 +1,11 @@
using System;
namespace weddingapi.Dtos
{
public record PersonDto
{
public string Name { get; init; }
public bool IsChild { get; set; }
}
}

13
Dtos/UpdateFamilyDto.cs Normal file
View File

@@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace weddingapi.Dtos
{
public record UpdateFamilyDto
{
[Required]
public string Name { get; init; }
[Required]
public IList<PersonDto> Members { get; init;}
}
}

21
Models/Family.cs Normal file
View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
namespace weddingapi.Models
{
public record Family
{
public Guid Id { get; init; }
public string Name { get; init; }
public DateTimeOffset CreatedDate { get; set; }
public IList<Person> Members { get; init; }
public Family()
{
this.Members = new List<Person>();
}
}
}

11
Models/Person.cs Normal file
View File

@@ -0,0 +1,11 @@
using System;
namespace weddingapi.Models
{
public record Person
{
public string Name { get; init; }
public bool IsChild { get; set; }
}
}

26
Program.cs Normal file
View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace weddingapi
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}

View File

@@ -0,0 +1,31 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:50382",
"sslPort": 44326
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"weddingapi": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

1
README.md Normal file
View File

@@ -0,0 +1 @@
https://www.youtube.com/watch?v=bgk8N_rx1F4

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using weddingapi.Models;
namespace weddingapi.Repositories
{
public interface IFamilyRepository
{
Task<Family> GetFamilyAsync(Guid id);
Task<IEnumerable<Family>> GetFamiliesAsync();
Task CreateFamilyAsync(Family family);
Task UpdateFamilyAsync(Family family);
Task DeleteFamilyAsync(Guid id);
}
}

View File

@@ -0,0 +1,44 @@
// using System;
// using System.Collections.Generic;
// using System.Linq;
// using weddingapi.Models;
// namespace weddingapi.Repositories
// {
// public class InMemFamilyRepository : IFamilyRepository
// {
// private readonly List<Family> families = new()
// {
// new Family { Id = Guid.NewGuid(), Name = "Lol", CreatedDate = DateTimeOffset.UtcNow },
// new Family { Id = Guid.NewGuid(), Name = "Lol1", CreatedDate = DateTimeOffset.UtcNow },
// new Family { Id = Guid.NewGuid(), Name = "Lol2", CreatedDate = DateTimeOffset.UtcNow }
// };
// public IEnumerable<Family> GetFamiliesAsync()
// {
// return families;
// }
// public Family GetFamilyAsync(Guid id)
// {
// return families.Where(family => family.Id == id).SingleOrDefault();
// }
// public void CreateFamilyAsync(Family family)
// {
// this.families.Add(family);
// }
// public void UpdateFamilyAsync(Family family)
// {
// var index = families.FindIndex(existingFamily => existingFamily.Id == family.Id);
// families[index] = family;
// }
// public void DeleteFamilyAsync(Guid id)
// {
// var index = families.FindIndex(existingFamily => existingFamily.Id == id);
// families.RemoveAt(index);
// }
// }
// }

View File

@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver;
using weddingapi.Models;
namespace weddingapi.Repositories
{
public class MongoDBFamilyRepository : IFamilyRepository
{
private const string databaseName = "wedding";
private const string collectionName = "families";
private readonly IMongoCollection<Family> familyCollection;
private readonly FilterDefinitionBuilder<Family> filterBuilder = Builders<Family>.Filter;
public MongoDBFamilyRepository(IMongoClient mongoClient)
{
IMongoDatabase database = mongoClient.GetDatabase(databaseName);
familyCollection = database.GetCollection<Family>(collectionName);
}
public async Task CreateFamilyAsync(Family family)
{
await familyCollection.InsertOneAsync(family);
}
public async Task DeleteFamilyAsync(Guid id)
{
var filter = filterBuilder.Eq(family => family.Id, id);
await familyCollection.DeleteOneAsync(filter);
}
public async Task<IEnumerable<Family>> GetFamiliesAsync()
{
var res = await familyCollection.FindAsync(new BsonDocument());
return await res.ToListAsync();
}
public async Task<Family> GetFamilyAsync(Guid id)
{
var filter = filterBuilder.Eq(family => family.Id, id);
var res = await familyCollection.FindAsync(filter);
return await res.SingleOrDefaultAsync();
}
public async Task UpdateFamilyAsync(Family family)
{
var filter = filterBuilder.Eq(exisitingFamily => exisitingFamily.Id, family.Id);
await familyCollection.ReplaceOneAsync(filter, family);
}
}
}

79
Startup.cs Normal file
View File

@@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;
using MongoDB.Driver;
using weddingapi.Config;
using weddingapi.Repositories;
namespace weddingapi
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
BsonSerializer.RegisterSerializer(new GuidSerializer(BsonType.String));
BsonSerializer.RegisterSerializer(new DateTimeOffsetSerializer(BsonType.String));
services.AddSingleton<IMongoClient>(serviceProvider =>
{
var config = Configuration.GetSection(nameof(MongoDbConfig)).Get<MongoDbConfig>();
return new MongoClient(config.ConnectionString);
});
services.AddSingleton<IFamilyRepository, MongoDBFamilyRepository>();
services.AddControllers(options => {
options.SuppressAsyncSuffixInActionNames = false;
});
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "weddingapi", Version = "v1" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "weddingapi v1"));
}
if(env.IsDevelopment())
{
app.UseHttpsRedirection();
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

15
WeatherForecast.cs Normal file
View File

@@ -0,0 +1,15 @@
using System;
namespace weddingapi
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string Summary { get; set; }
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

14
appsettings.json Normal file
View File

@@ -0,0 +1,14 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"MongoDbConfig": {
"Host": "localhost",
"Port": "27017"
}
}

View File

@@ -0,0 +1,39 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: weddingapi-deployment
spec:
selector:
matchLabels:
app: weddingapi
template:
metadata:
labels:
app: weddingapi
spec:
containers:
- name: weddingapi
image: weddingapi:v1
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
env:
- name: MongoDbConfig__Host
value: mongodb-service
---
apiVersion: v1
kind: Service
metadata:
name: weddingapi-service
spec:
type: LoadBalancer
selector:
app: weddingapi
ports:
- port: 11180 #external port
targetPort: 80 #container port

12
weddingapi.csproj Normal file
View File

@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MongoDB.Driver" Version="2.14.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
</ItemGroup>
</Project>