Skip to content

Commit a2f582c

Browse files
authored
TodoApp Service (#19)
1 parent cb8cab2 commit a2f582c

File tree

15 files changed

+1070
-0
lines changed

15 files changed

+1070
-0
lines changed

samples/datasync-server/.gitignore

Lines changed: 633 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.0.31903.59
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4B8E4DD5-C2CA-4729-A10B-8A3C993018EC}"
7+
EndProject
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Datasync.Server", "src\Sample.Datasync.Server\Sample.Datasync.Server.csproj", "{67A76156-0033-4085-86BE-558DC28688A6}"
9+
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "library", "library", "{95358590-6440-469A-8A6A-6ACC47F52966}"
11+
EndProject
12+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Datasync.Server", "..\..\..\..\CommunityToolkit\Datasync\src\CommunityToolkit.Datasync.Server\CommunityToolkit.Datasync.Server.csproj", "{D42DBA41-28AA-4B6D-83A4-A5B839EC8B25}"
13+
EndProject
14+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Datasync.Server.Abstractions", "..\..\..\..\CommunityToolkit\Datasync\src\CommunityToolkit.Datasync.Server.Abstractions\CommunityToolkit.Datasync.Server.Abstractions.csproj", "{CECB1B21-46C7-4197-B8D2-EC54D0AF8145}"
15+
EndProject
16+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Datasync.Server.EntityFrameworkCore", "..\..\..\..\CommunityToolkit\Datasync\src\CommunityToolkit.Datasync.Server.EntityFrameworkCore\CommunityToolkit.Datasync.Server.EntityFrameworkCore.csproj", "{8CDD3736-4B8D-43F5-8F9A-222210D73DAC}"
17+
EndProject
18+
Global
19+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
20+
Debug|Any CPU = Debug|Any CPU
21+
Release|Any CPU = Release|Any CPU
22+
EndGlobalSection
23+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
24+
{67A76156-0033-4085-86BE-558DC28688A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25+
{67A76156-0033-4085-86BE-558DC28688A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
26+
{67A76156-0033-4085-86BE-558DC28688A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
27+
{67A76156-0033-4085-86BE-558DC28688A6}.Release|Any CPU.Build.0 = Release|Any CPU
28+
{D42DBA41-28AA-4B6D-83A4-A5B839EC8B25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29+
{D42DBA41-28AA-4B6D-83A4-A5B839EC8B25}.Debug|Any CPU.Build.0 = Debug|Any CPU
30+
{D42DBA41-28AA-4B6D-83A4-A5B839EC8B25}.Release|Any CPU.ActiveCfg = Release|Any CPU
31+
{D42DBA41-28AA-4B6D-83A4-A5B839EC8B25}.Release|Any CPU.Build.0 = Release|Any CPU
32+
{CECB1B21-46C7-4197-B8D2-EC54D0AF8145}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33+
{CECB1B21-46C7-4197-B8D2-EC54D0AF8145}.Debug|Any CPU.Build.0 = Debug|Any CPU
34+
{CECB1B21-46C7-4197-B8D2-EC54D0AF8145}.Release|Any CPU.ActiveCfg = Release|Any CPU
35+
{CECB1B21-46C7-4197-B8D2-EC54D0AF8145}.Release|Any CPU.Build.0 = Release|Any CPU
36+
{8CDD3736-4B8D-43F5-8F9A-222210D73DAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37+
{8CDD3736-4B8D-43F5-8F9A-222210D73DAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
38+
{8CDD3736-4B8D-43F5-8F9A-222210D73DAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
39+
{8CDD3736-4B8D-43F5-8F9A-222210D73DAC}.Release|Any CPU.Build.0 = Release|Any CPU
40+
EndGlobalSection
41+
GlobalSection(SolutionProperties) = preSolution
42+
HideSolutionNode = FALSE
43+
EndGlobalSection
44+
GlobalSection(NestedProjects) = preSolution
45+
{67A76156-0033-4085-86BE-558DC28688A6} = {4B8E4DD5-C2CA-4729-A10B-8A3C993018EC}
46+
{D42DBA41-28AA-4B6D-83A4-A5B839EC8B25} = {95358590-6440-469A-8A6A-6ACC47F52966}
47+
{CECB1B21-46C7-4197-B8D2-EC54D0AF8145} = {95358590-6440-469A-8A6A-6ACC47F52966}
48+
{8CDD3736-4B8D-43F5-8F9A-222210D73DAC} = {95358590-6440-469A-8A6A-6ACC47F52966}
49+
EndGlobalSection
50+
EndGlobal

samples/datasync-server/azure.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json
2+
3+
name: community-toolkit-datasync-server
4+
metadata:
5+
6+
7+
workflows:
8+
up:
9+
steps:
10+
- azd: provision
11+
- azd: deploy --all
12+
13+
services:
14+
backend:
15+
project: ./src/Sample.Datasync.Server
16+
language: csharp
17+
host: appservice
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
targetScope = 'subscription'
6+
7+
@minLength(1)
8+
@maxLength(64)
9+
@description('Name of the the environment which is used to generate a short unique hash used in all resources.')
10+
param environmentName string
11+
12+
@minLength(1)
13+
@description('Primary location for all resources')
14+
param location string
15+
16+
@description('Optional - the name of the App Service to create. If not provided, a unique name will be generated.')
17+
param appServiceName string = ''
18+
19+
@description('Optional - the name of the App Service Plan to create. If not provided, a unique name will be generated.')
20+
param appServicePlanName string = ''
21+
22+
@description('Optional - the name of the Resource Group to create. If not provided, a unique name will be generated.')
23+
param resourceGroupName string = ''
24+
25+
@description('Optional - the name of the SQL Server to create. If not provided, a unique name will be generated.')
26+
param sqlServerName string = ''
27+
28+
@description('Optional - the name of the SQL Database to create. If not provided, a unique name will be generated.')
29+
param sqlDatabaseName string = ''
30+
31+
@description('Optional - the SQL Server administrator password. If not provided, the username will be \'appadmin\'.')
32+
param sqlAdminUsername string = 'appadmin'
33+
34+
@secure()
35+
@description('Optional - SQL Server administrator password. If not provided, a random password will be generated.')
36+
param sqlAdminPassword string = newGuid()
37+
38+
var resourceToken = toLower(uniqueString(subscription().id, environmentName, location))
39+
var tags = { 'azd-env-name': environmentName }
40+
41+
resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = {
42+
name: !empty(resourceGroupName) ? resourceGroupName : 'rg-${environmentName}'
43+
location: location
44+
tags: tags
45+
}
46+
47+
module resources './resources.bicep' = {
48+
name: 'resources'
49+
scope: resourceGroup
50+
params: {
51+
location: location
52+
tags: tags
53+
appServiceName: !empty(appServiceName) ? appServiceName : 'app-${resourceToken}'
54+
appServicePlanName: !empty(appServicePlanName) ? appServicePlanName : 'asp-${resourceToken}'
55+
sqlServerName: !empty(sqlServerName) ? sqlServerName : 'sql-${resourceToken}'
56+
sqlDatabaseName: !empty(sqlDatabaseName) ? sqlDatabaseName : 'TodoDb'
57+
sqlAdminUsername: !empty(sqlAdminUsername) ? sqlAdminUsername : 'appadmin'
58+
sqlAdminPassword: sqlAdminPassword
59+
}
60+
}
61+
62+
output SERVICE_ENDPOINT string = resources.outputs.SERVICE_ENDPOINT
63+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3+
"contentVersion": "1.0.0.0",
4+
"parameters": {
5+
"environmentName": {
6+
"value": "${AZURE_ENV_NAME}"
7+
},
8+
"location": {
9+
"value": "${AZURE_LOCATION}"
10+
},
11+
"principalId": {
12+
"value": "${AZURE_PRINCIPAL_ID}"
13+
},
14+
"sqlAdminUsername": {
15+
"value": "appadmin"
16+
},
17+
"sqlAdminPassword": {
18+
"value": "$(secretOrRandomPassword)"
19+
}
20+
}
21+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
targetScope = 'resourceGroup'
2+
3+
@minLength(1)
4+
@description('Primary location for all resources')
5+
param location string
6+
7+
@description('The name of the App Service to create.')
8+
param appServiceName string
9+
10+
@description('The name of the App Service Plan to create.')
11+
param appServicePlanName string
12+
13+
@description('The name of the SQL Server to create.')
14+
param sqlServerName string
15+
16+
@description('The name of the SQL Database to create.')
17+
param sqlDatabaseName string
18+
19+
@description('The SQL Server administrator password.')
20+
param sqlAdminUsername string
21+
22+
@secure()
23+
@description('The SQL Server administrator password.')
24+
param sqlAdminPassword string
25+
26+
@description('The list of tags to apply to all resources.')
27+
param tags object = {}
28+
29+
resource sqlServer 'Microsoft.Sql/servers@2021-11-01' = {
30+
name: sqlServerName
31+
location: location
32+
tags: tags
33+
properties: {
34+
version: '12.0'
35+
minimalTlsVersion: '1.2'
36+
publicNetworkAccess: 'Enabled'
37+
administratorLogin: sqlAdminUsername
38+
administratorLoginPassword: sqlAdminPassword
39+
}
40+
41+
resource firewall 'firewallRules' = {
42+
name: 'AllowAllAzureServices'
43+
properties: {
44+
startIpAddress: '0.0.0.0'
45+
endIpAddress: '0.0.0.0'
46+
}
47+
}
48+
}
49+
50+
resource sqlDatabase 'Microsoft.Sql/servers/databases@2021-11-01' = {
51+
name: sqlDatabaseName
52+
parent: sqlServer
53+
location: location
54+
tags: tags
55+
sku: {
56+
name: 'Basic'
57+
}
58+
properties: {
59+
collation: 'SQL_Latin1_General_CP1_CI_AS'
60+
maxSizeBytes: 1073741824
61+
}
62+
}
63+
64+
resource appServicePlan 'Microsoft.Web/serverfarms@2022-09-01' = {
65+
name: appServicePlanName
66+
location: location
67+
tags: tags
68+
sku: {
69+
name: 'B1'
70+
capacity: 1
71+
}
72+
}
73+
74+
resource appService 'Microsoft.Web/sites@2022-09-01' = {
75+
name: appServiceName
76+
location: location
77+
tags: union(tags, {
78+
'azd-service-name': 'backend'
79+
'hidden-related:${appServicePlan.id}': 'empty'
80+
})
81+
properties: {
82+
httpsOnly: true
83+
serverFarmId: appServicePlan.id
84+
siteConfig: {
85+
ftpsState: 'Disabled'
86+
minTlsVersion: '1.2'
87+
}
88+
}
89+
90+
resource configLogs 'config' = {
91+
name: 'logs'
92+
properties: {
93+
applicationLogs: { fileSystem: { level: 'Verbose' } }
94+
detailedErrorMessages: { enabled: true }
95+
failedRequestsTracing: { enabled: true }
96+
httpLogs: { fileSystem: { retentionInMb: 35, retentionInDays: 3, enabled: true } }
97+
}
98+
}
99+
100+
resource connectionStrings 'config' = {
101+
name: 'connectionstrings'
102+
properties: {
103+
DefaultConnection: {
104+
value: 'Data Source=tcp:${sqlServer.properties.fullyQualifiedDomainName},1433;Initial Catalog=${sqlDatabase.name};User Id=${sqlServer.properties.administratorLogin};Password=${sqlAdminPassword};'
105+
type: 'SQLAzure'
106+
}
107+
}
108+
}
109+
}
110+
111+
output SERVICE_ENDPOINT string = 'https://${appService.properties.defaultHostName}'
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using CommunityToolkit.Datasync.Server;
6+
using CommunityToolkit.Datasync.Server.EntityFrameworkCore;
7+
using Microsoft.AspNetCore.Mvc;
8+
using Sample.Datasync.Server.Db;
9+
10+
namespace Sample.Datasync.Server.Controllers;
11+
12+
[Route("tables/[controller]")]
13+
public class TodoItemController : TableController<TodoItem>
14+
{
15+
public TodoItemController(AppDbContext context)
16+
: base(new EntityTableRepository<TodoItem>(context))
17+
{
18+
}
19+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using Microsoft.EntityFrameworkCore;
6+
7+
namespace Sample.Datasync.Server.Db;
8+
9+
public class AppDbContext : DbContext
10+
{
11+
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
12+
{
13+
14+
}
15+
16+
public DbSet<TodoItem> TodoItems => Set<TodoItem>();
17+
18+
public async Task InitializeDatabaseAsync()
19+
{
20+
await Database.EnsureCreatedAsync();
21+
}
22+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using CommunityToolkit.Datasync.Server.EntityFrameworkCore;
6+
using System.ComponentModel.DataAnnotations;
7+
8+
namespace Sample.Datasync.Server.Db;
9+
10+
public class TodoItem : EntityTableData
11+
{
12+
[Required, MinLength(1)]
13+
public string Text { get; set; } = string.Empty;
14+
15+
public bool IsComplete { get; set; }
16+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using CommunityToolkit.Datasync.Server;
6+
using Microsoft.EntityFrameworkCore;
7+
using Sample.Datasync.Server.Db;
8+
9+
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
10+
11+
string connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
12+
?? throw new ApplicationException("DefaultConnection is not set");
13+
14+
builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(connectionString));
15+
builder.Services.AddDatasyncServices();
16+
builder.Services.AddControllers();
17+
18+
WebApplication app = builder.Build();
19+
20+
// Initialize the database
21+
using (IServiceScope scope = app.Services.CreateScope())
22+
{
23+
AppDbContext context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
24+
await context.InitializeDatabaseAsync();
25+
}
26+
27+
app.UseHttpsRedirection();
28+
app.UseAuthorization();
29+
app.MapControllers();
30+
31+
app.Run();

0 commit comments

Comments
 (0)