Skip to content

Commit a8b6176

Browse files
Merge pull request #153 from NielsPilgaard/chat
Working Chat
2 parents 0c57972 + 70e41c5 commit a8b6176

File tree

106 files changed

+4228
-1315
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+4228
-1315
lines changed

.github/workflows/container_apps_chat_cd.yml

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Test & Publish Chat Docker Image to Docker Hub
1+
name: Chat - Publish Docker Image
22

33
on:
44
push:
@@ -13,7 +13,6 @@ jobs:
1313
env:
1414
MSSQL_SA_PASSWORD: 6efe173b-3e33-4d6c-8f50-3e5f7cadd54c
1515
TEST_DIRECTORY: tests/container_apps/Jordnaer.Chat.Tests
16-
DOCKERFILE_PATH: src/container_apps/Jordnaer.Chat/Dockerfile
1716
services:
1817
mssql:
1918
image: mcr.microsoft.com/mssql/server:2019-latest
@@ -30,42 +29,47 @@ jobs:
3029
uses: actions/setup-dotnet@v3
3130
with:
3231
dotnet-version: '7.x'
33-
include-prerelease: true
3432

3533
- name: Test
3634
run: dotnet test "${{ env.TEST_DIRECTORY }}" --filter Category!=ManualTest
3735
env:
3836
ConnectionStrings__AppConfig: ${{ secrets.AZURE_APP_CONFIGURATION_CONNECTION_STRING }}
3937
ConnectionStrings__JordnaerDbContext: "Server=localhost,1433;Database=jordnaer;User ID=sa;Password=${{ env.MSSQL_SA_PASSWORD }};Persist Security Info=False;TrustServerCertificate=true;"
4038

41-
build:
39+
push:
40+
if: |
41+
always() &&
42+
(needs.test.result == 'success' || needs.test.result == 'skipped')
43+
needs: [test]
4244
runs-on: ubuntu-latest
4345
env:
44-
IMAGE_NAME: chat
45-
IMAGE_VERSION: latest
46+
DOCKER_IMAGE_NAME: chat
4647
steps:
4748

4849
- name: Checkout code
4950
uses: actions/checkout@v3
5051

5152
# Apply tags and labels based on GitHub Actions events and Git metadata
52-
- name: Docker meta
53+
- name: Apply Docker Image tags & labels
5354
id: meta
5455
uses: docker/metadata-action@v4
5556
with:
5657
# list of Docker images to use as base name for tags
5758
images: |
58-
${{ github.repository_owner }}/${{ env.IMAGE_NAME }}
59-
ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}
59+
ghcr.io/${{ github.repository_owner }}/${{ env.DOCKER_IMAGE_NAME }}
6060
# generate Docker tags based on the following events/attributes
6161
tags: |
6262
type=ref,event=branch
6363
type=ref,event=pr
64-
type=semver,pattern={{version}}
65-
type=semver,pattern={{major}}.{{minor}}
66-
type=semver,pattern={{major}}
64+
type=match,pattern=v(.*),group=1
65+
type=match,pattern=v(\d.\d),group=1
66+
type=match,pattern=v(\d),group=1
6767
type=sha
68-
68+
type=raw,value=latest,enable={{is_default_branch}}
69+
70+
- name: Set up QEMU
71+
uses: docker/setup-qemu-action@v2
72+
6973
- name: Set up Docker Buildx
7074
uses: docker/setup-buildx-action@v2
7175

@@ -79,8 +83,15 @@ jobs:
7983
- name: Build and push
8084
uses: docker/build-push-action@v4
8185
with:
82-
context: .
8386
push: true
84-
file: ${{ env.DOCKERFILE_PATH }}
87+
context: .
88+
file: ./src/container_apps/Jordnaer.Chat/Dockerfile
8589
tags: ${{ steps.meta.outputs.tags }}
8690
labels: ${{ steps.meta.outputs.labels }}
91+
92+
- name: Deploy pushed image to Azure Container Apps
93+
uses: azure/container-apps-deploy-action@v1
94+
with:
95+
containerAppName: chat
96+
resourceGroup: Jordnaer
97+
imageToDeploy: ghcr.io/nielspilgaard/chat:${{ github.sha }}

.github/workflows/container_apps_chat_ci.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Test Chat
1+
name: Chat - Run Tests
22

33
on:
44
push:
@@ -35,7 +35,6 @@ jobs:
3535
uses: actions/setup-dotnet@v3
3636
with:
3737
dotnet-version: '7.x'
38-
include-prerelease: true
3938

4039
- name: Test
4140
# Dependabot cannot access secrets, so we disable this step for Dependabot
Lines changed: 55 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,55 @@
1-
name: Test Website Backend
2-
3-
on:
4-
push:
5-
paths:
6-
- 'src/web/**'
7-
- 'tests/web/**'
8-
- '.github/workflows/website_ci.yml'
9-
workflow_dispatch:
10-
11-
# Needed by dorny/test-reporter@v1
12-
permissions:
13-
statuses: write
14-
checks: write
15-
16-
env:
17-
MSSQL_SA_PASSWORD: 6efe173b-3e33-4d6c-8f50-3e5f7cadd54c
18-
WORKING_DIRECTORY: tests/web/Jordnaer.Server.Tests
19-
jobs:
20-
test:
21-
runs-on: ubuntu-latest
22-
services:
23-
mssql:
24-
image: mcr.microsoft.com/mssql/server:2019-latest
25-
env:
26-
SA_PASSWORD: "${{ env.MSSQL_SA_PASSWORD }}"
27-
ACCEPT_EULA: 'Y'
28-
ports:
29-
- 1433:1433
30-
steps:
31-
- name: Checkout
32-
uses: actions/checkout@v3
33-
34-
- name: Set up dotnet
35-
uses: actions/setup-dotnet@v3
36-
with:
37-
dotnet-version: '7.x'
38-
include-prerelease: true
39-
40-
- name: Test
41-
# Dependabot cannot access secrets, so we disable this step for Dependabot
42-
if: github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]'
43-
run: dotnet test "${{ env.WORKING_DIRECTORY }}" --logger "trx;LogFileName=test-results.trx" --filter Category!=ManualTest
44-
env:
45-
ConnectionStrings__AppConfig: ${{ secrets.AZURE_APP_CONFIGURATION_CONNECTION_STRING }}
46-
ConnectionStrings__JordnaerDbContext: "Server=localhost,1433;Database=jordnaer;User ID=sa;Password=${{ env.MSSQL_SA_PASSWORD }};Persist Security Info=False;TrustServerCertificate=true;"
47-
48-
- name: Test Report
49-
# Dependabot cannot access secrets, so we disable this step for Dependabot
50-
if: (success() || failure()) && (github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]')
51-
uses: dorny/test-reporter@v1
52-
with:
53-
name: Test Report
54-
path: "**.trx"
55-
reporter: dotnet-trx
1+
name: Website Backend - Run Tests
2+
3+
on:
4+
push:
5+
paths:
6+
- 'src/web/**'
7+
- 'tests/web/**'
8+
- '.github/workflows/website_backend_ci.yml'
9+
workflow_dispatch:
10+
11+
# Needed by dorny/test-reporter@v1
12+
permissions:
13+
statuses: write
14+
checks: write
15+
16+
env:
17+
MSSQL_SA_PASSWORD: 6efe173b-3e33-4d6c-8f50-3e5f7cadd54c
18+
WORKING_DIRECTORY: tests/web/Jordnaer.Server.Tests
19+
jobs:
20+
test:
21+
runs-on: ubuntu-latest
22+
services:
23+
mssql:
24+
image: mcr.microsoft.com/mssql/server:2019-latest
25+
env:
26+
SA_PASSWORD: "${{ env.MSSQL_SA_PASSWORD }}"
27+
ACCEPT_EULA: 'Y'
28+
ports:
29+
- 1433:1433
30+
steps:
31+
- name: Checkout
32+
uses: actions/checkout@v3
33+
34+
- name: Set up dotnet
35+
uses: actions/setup-dotnet@v3
36+
with:
37+
dotnet-version: '7.x'
38+
include-prerelease: true
39+
40+
- name: Test
41+
# Dependabot cannot access secrets, so we disable this step for Dependabot
42+
if: github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]'
43+
run: dotnet test "${{ env.WORKING_DIRECTORY }}" --logger "trx;LogFileName=test-results.trx" --filter Category!=ManualTest
44+
env:
45+
ConnectionStrings__AppConfig: ${{ secrets.AZURE_APP_CONFIGURATION_CONNECTION_STRING }}
46+
ConnectionStrings__JordnaerDbContext: "Server=localhost,1433;Database=jordnaer;User ID=sa;Password=${{ env.MSSQL_SA_PASSWORD }};Persist Security Info=False;TrustServerCertificate=true;"
47+
48+
- name: Test Report
49+
# Dependabot cannot access secrets, so we disable this step for Dependabot
50+
if: (success() || failure()) && (github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]')
51+
uses: dorny/test-reporter@v1
52+
with:
53+
name: Test Report
54+
path: "**.trx"
55+
reporter: dotnet-trx

.github/workflows/website_cd.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Build, test and deploy to Azure App Service
1+
name: Website - Deploy
22

33
on:
44
push:
@@ -18,7 +18,7 @@ env:
1818
WORKING_DIRECTORY: src/web/Server
1919
MSSQL_SA_PASSWORD: 6efe173b-3e33-4d6c-8f50-3e5f7cadd54c
2020
jobs:
21-
build:
21+
deploy:
2222
runs-on: ubuntu-latest
2323
services:
2424
mssql:
@@ -36,7 +36,6 @@ jobs:
3636
uses: actions/setup-dotnet@v3
3737
with:
3838
dotnet-version: '7.x'
39-
include-prerelease: true
4039

4140
- name: Restore Nugets
4241
run: dotnet restore "${{ env.WORKING_DIRECTORY }}"

benchmarks/Jordnaer.Server.Benchmarks/UserSearchBenchmark.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace Jordnaer.Server.Benchmarks;
1212
[MemoryDiagnoser]
1313
public class UserSearchBenchmark
1414
{
15-
private IUserSearchApiClient _client = null!;
15+
private IUserSearchClient _client = null!;
1616
private JordnaerDbContext _context = null!;
1717
private UserProfile _randomUser = null!;
1818
private List<LookingFor> _lookingFor = new();
@@ -43,7 +43,7 @@ public async Task GlobalSetupAsync()
4343
.Take(1)
4444
.FirstOrDefaultAsync())!;
4545

46-
_client = RestService.For<IUserSearchApiClient>(httpClient);
46+
_client = RestService.For<IUserSearchClient>(httpClient);
4747
}
4848

4949
[Benchmark]
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
2+
using Jordnaer.Shared;
3+
using Microsoft.EntityFrameworkCore;
4+
5+
namespace Jordnaer.Chat;
6+
7+
public class ChatDbContext : DbContext
8+
{
9+
public DbSet<UserProfile> UserProfiles { get; set; } = default!;
10+
public DbSet<Shared.Chat> Chats { get; set; } = default!;
11+
public DbSet<ChatMessage> ChatMessages { get; set; } = default!;
12+
public DbSet<UserChat> UserChats { get; set; } = default!;
13+
public DbSet<UnreadMessage> UnreadMessages { get; set; } = default;
14+
15+
protected override void OnModelCreating(ModelBuilder modelBuilder)
16+
{
17+
modelBuilder.Entity<UserProfile>()
18+
.HasMany(userProfile => userProfile.LookingFor)
19+
.WithMany()
20+
.UsingEntity<UserProfileLookingFor>();
21+
22+
modelBuilder.Entity<UserProfile>()
23+
.HasMany(userProfile => userProfile.Contacts)
24+
.WithMany()
25+
.UsingEntity<UserContact>(
26+
builder => builder
27+
.HasOne<UserProfile>()
28+
.WithMany()
29+
.HasForeignKey(userContact => userContact.UserProfileId),
30+
builder => builder
31+
.HasOne<UserProfile>()
32+
.WithMany()
33+
.HasForeignKey(userContact => userContact.ContactId));
34+
35+
modelBuilder.Entity<Shared.Chat>()
36+
.HasMany(userProfile => userProfile.Recipients)
37+
.WithMany()
38+
.UsingEntity<UserChat>();
39+
40+
modelBuilder.Entity<UserProfile>()
41+
.Property(user => user.SearchableName)
42+
.HasComputedColumnSql(
43+
$"[{nameof(UserProfile.FirstName)}] + " +
44+
$"[{nameof(UserProfile.LastName)}] + " +
45+
$"[{nameof(UserProfile.UserName)}]",
46+
stored: true);
47+
48+
49+
modelBuilder.Entity<UserProfile>()
50+
.Property(user => user.Age)
51+
.HasComputedColumnSql(
52+
$"DATEDIFF(YY, [{nameof(UserProfile.DateOfBirth)}], GETDATE()) - " +
53+
$"CASE WHEN MONTH([{nameof(UserProfile.DateOfBirth)}]) > MONTH(GETDATE()) " +
54+
$"OR MONTH([{nameof(UserProfile.DateOfBirth)}]) = MONTH(GETDATE()) " +
55+
$"AND DAY([{nameof(UserProfile.DateOfBirth)}]) > DAY(GETDATE()) " +
56+
$"THEN 1 ELSE 0 END");
57+
58+
modelBuilder.Entity<ChildProfile>()
59+
.Property(child => child.Age)
60+
.HasComputedColumnSql(
61+
$"DATEDIFF(YY, [{nameof(ChildProfile.DateOfBirth)}], GETDATE()) - " +
62+
$"CASE WHEN MONTH([{nameof(ChildProfile.DateOfBirth)}]) > MONTH(GETDATE()) " +
63+
$"OR MONTH([{nameof(ChildProfile.DateOfBirth)}]) = MONTH(GETDATE()) " +
64+
$"AND DAY([{nameof(ChildProfile.DateOfBirth)}]) > DAY(GETDATE()) " +
65+
$"THEN 1 ELSE 0 END");
66+
67+
base.OnModelCreating(modelBuilder);
68+
}
69+
70+
public ChatDbContext(DbContextOptions<ChatDbContext> options) : base(options)
71+
{ }
72+
}

src/container_apps/Jordnaer.Chat/Dockerfile

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ EXPOSE 443
88
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
99
WORKDIR /src
1010
COPY ["Directory.Build.props", "."]
11-
COPY ["src/container_apps/Chat/Chat.csproj", "src/container_apps/Chat/"]
12-
RUN dotnet restore "src/container_apps/Chat/Chat.csproj"
11+
COPY ["src/container_apps/Jordnaer.Chat/Jordnaer.Chat.csproj", "src/container_apps/Jordnaer.Chat/"]
12+
RUN dotnet restore "src/container_apps/Jordnaer.Chat/Jordnaer.Chat.csproj"
1313
COPY . .
14-
WORKDIR "/src/container_apps/Chat"
15-
RUN dotnet build "Chat.csproj" -c Release -o /app/build
14+
WORKDIR "/src/src/container_apps/Jordnaer.Chat"
15+
RUN dotnet build "Jordnaer.Chat.csproj" -c Release -o /app/build
1616

1717
FROM build AS publish
18-
RUN dotnet publish "Chat.csproj" -c Release -o /app/publish /p:UseAppHost=false
18+
RUN dotnet publish "Jordnaer.Chat.csproj" -c Release -o /app/publish /p:UseAppHost=false
1919

2020
FROM base AS final
2121
WORKDIR /app
2222
COPY --from=publish /app/publish .
23-
ENTRYPOINT ["dotnet", "Chat.dll"]
23+
ENTRYPOINT ["dotnet", "Jordnaer.Chat.dll"]
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
namespace Jordnaer.Chat;
2+
3+
public static class AzureAppConfigurationExtensions
4+
{
5+
public static WebApplicationBuilder AddAzureAppConfiguration(this WebApplicationBuilder builder)
6+
{
7+
string? connectionString = builder.Configuration.GetConnectionString("AppConfig");
8+
if (connectionString is null)
9+
{
10+
return builder.Environment.IsDevelopment()
11+
? builder
12+
: throw new InvalidOperationException("Connection string 'AppConfig' not found.");
13+
}
14+
15+
builder.Services.AddAzureAppConfiguration();
16+
builder.Configuration.AddAzureAppConfiguration(options =>
17+
options.Connect(connectionString)
18+
// Load all keys that have no label
19+
.Select("*")
20+
// Configure to reload builder if the registered sentinel key is modified
21+
.ConfigureRefresh(refreshOptions =>
22+
{
23+
refreshOptions.Register("Sentinel", refreshAll: true);
24+
refreshOptions.SetCacheExpiration(TimeSpan.FromMinutes(5));
25+
})
26+
.UseFeatureFlags(flagOptions => flagOptions.CacheExpirationInterval = TimeSpan.FromMinutes(3)));
27+
28+
return builder;
29+
}
30+
}

0 commit comments

Comments
 (0)