Skip to content

Commit

Permalink
TodoApp Service (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianhall authored May 10, 2024
1 parent cb8cab2 commit a2f582c
Show file tree
Hide file tree
Showing 15 changed files with 1,070 additions and 0 deletions.
633 changes: 633 additions & 0 deletions samples/datasync-server/.gitignore

Large diffs are not rendered by default.

50 changes: 50 additions & 0 deletions samples/datasync-server/Sample.Datasync.Server.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4B8E4DD5-C2CA-4729-A10B-8A3C993018EC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Datasync.Server", "src\Sample.Datasync.Server\Sample.Datasync.Server.csproj", "{67A76156-0033-4085-86BE-558DC28688A6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "library", "library", "{95358590-6440-469A-8A6A-6ACC47F52966}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Datasync.Server", "..\..\..\..\CommunityToolkit\Datasync\src\CommunityToolkit.Datasync.Server\CommunityToolkit.Datasync.Server.csproj", "{D42DBA41-28AA-4B6D-83A4-A5B839EC8B25}"
EndProject
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}"
EndProject
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}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{67A76156-0033-4085-86BE-558DC28688A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{67A76156-0033-4085-86BE-558DC28688A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{67A76156-0033-4085-86BE-558DC28688A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{67A76156-0033-4085-86BE-558DC28688A6}.Release|Any CPU.Build.0 = Release|Any CPU
{D42DBA41-28AA-4B6D-83A4-A5B839EC8B25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D42DBA41-28AA-4B6D-83A4-A5B839EC8B25}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D42DBA41-28AA-4B6D-83A4-A5B839EC8B25}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D42DBA41-28AA-4B6D-83A4-A5B839EC8B25}.Release|Any CPU.Build.0 = Release|Any CPU
{CECB1B21-46C7-4197-B8D2-EC54D0AF8145}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CECB1B21-46C7-4197-B8D2-EC54D0AF8145}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CECB1B21-46C7-4197-B8D2-EC54D0AF8145}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CECB1B21-46C7-4197-B8D2-EC54D0AF8145}.Release|Any CPU.Build.0 = Release|Any CPU
{8CDD3736-4B8D-43F5-8F9A-222210D73DAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8CDD3736-4B8D-43F5-8F9A-222210D73DAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8CDD3736-4B8D-43F5-8F9A-222210D73DAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8CDD3736-4B8D-43F5-8F9A-222210D73DAC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{67A76156-0033-4085-86BE-558DC28688A6} = {4B8E4DD5-C2CA-4729-A10B-8A3C993018EC}
{D42DBA41-28AA-4B6D-83A4-A5B839EC8B25} = {95358590-6440-469A-8A6A-6ACC47F52966}
{CECB1B21-46C7-4197-B8D2-EC54D0AF8145} = {95358590-6440-469A-8A6A-6ACC47F52966}
{8CDD3736-4B8D-43F5-8F9A-222210D73DAC} = {95358590-6440-469A-8A6A-6ACC47F52966}
EndGlobalSection
EndGlobal
17 changes: 17 additions & 0 deletions samples/datasync-server/azure.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json

name: community-toolkit-datasync-server
metadata:
template: [email protected]

workflows:
up:
steps:
- azd: provision
- azd: deploy --all

services:
backend:
project: ./src/Sample.Datasync.Server
language: csharp
host: appservice
63 changes: 63 additions & 0 deletions samples/datasync-server/infra/main.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

targetScope = 'subscription'

@minLength(1)
@maxLength(64)
@description('Name of the the environment which is used to generate a short unique hash used in all resources.')
param environmentName string

@minLength(1)
@description('Primary location for all resources')
param location string

@description('Optional - the name of the App Service to create. If not provided, a unique name will be generated.')
param appServiceName string = ''

@description('Optional - the name of the App Service Plan to create. If not provided, a unique name will be generated.')
param appServicePlanName string = ''

@description('Optional - the name of the Resource Group to create. If not provided, a unique name will be generated.')
param resourceGroupName string = ''

@description('Optional - the name of the SQL Server to create. If not provided, a unique name will be generated.')
param sqlServerName string = ''

@description('Optional - the name of the SQL Database to create. If not provided, a unique name will be generated.')
param sqlDatabaseName string = ''

@description('Optional - the SQL Server administrator password. If not provided, the username will be \'appadmin\'.')
param sqlAdminUsername string = 'appadmin'

@secure()
@description('Optional - SQL Server administrator password. If not provided, a random password will be generated.')
param sqlAdminPassword string = newGuid()

var resourceToken = toLower(uniqueString(subscription().id, environmentName, location))
var tags = { 'azd-env-name': environmentName }

resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = {
name: !empty(resourceGroupName) ? resourceGroupName : 'rg-${environmentName}'
location: location
tags: tags
}

module resources './resources.bicep' = {
name: 'resources'
scope: resourceGroup
params: {
location: location
tags: tags
appServiceName: !empty(appServiceName) ? appServiceName : 'app-${resourceToken}'
appServicePlanName: !empty(appServicePlanName) ? appServicePlanName : 'asp-${resourceToken}'
sqlServerName: !empty(sqlServerName) ? sqlServerName : 'sql-${resourceToken}'
sqlDatabaseName: !empty(sqlDatabaseName) ? sqlDatabaseName : 'TodoDb'
sqlAdminUsername: !empty(sqlAdminUsername) ? sqlAdminUsername : 'appadmin'
sqlAdminPassword: sqlAdminPassword
}
}

output SERVICE_ENDPOINT string = resources.outputs.SERVICE_ENDPOINT

21 changes: 21 additions & 0 deletions samples/datasync-server/infra/main.parameters.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"environmentName": {
"value": "${AZURE_ENV_NAME}"
},
"location": {
"value": "${AZURE_LOCATION}"
},
"principalId": {
"value": "${AZURE_PRINCIPAL_ID}"
},
"sqlAdminUsername": {
"value": "appadmin"
},
"sqlAdminPassword": {
"value": "$(secretOrRandomPassword)"
}
}
}
111 changes: 111 additions & 0 deletions samples/datasync-server/infra/resources.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
targetScope = 'resourceGroup'

@minLength(1)
@description('Primary location for all resources')
param location string

@description('The name of the App Service to create.')
param appServiceName string

@description('The name of the App Service Plan to create.')
param appServicePlanName string

@description('The name of the SQL Server to create.')
param sqlServerName string

@description('The name of the SQL Database to create.')
param sqlDatabaseName string

@description('The SQL Server administrator password.')
param sqlAdminUsername string

@secure()
@description('The SQL Server administrator password.')
param sqlAdminPassword string

@description('The list of tags to apply to all resources.')
param tags object = {}

resource sqlServer 'Microsoft.Sql/servers@2021-11-01' = {
name: sqlServerName
location: location
tags: tags
properties: {
version: '12.0'
minimalTlsVersion: '1.2'
publicNetworkAccess: 'Enabled'
administratorLogin: sqlAdminUsername
administratorLoginPassword: sqlAdminPassword
}

resource firewall 'firewallRules' = {
name: 'AllowAllAzureServices'
properties: {
startIpAddress: '0.0.0.0'
endIpAddress: '0.0.0.0'
}
}
}

resource sqlDatabase 'Microsoft.Sql/servers/databases@2021-11-01' = {
name: sqlDatabaseName
parent: sqlServer
location: location
tags: tags
sku: {
name: 'Basic'
}
properties: {
collation: 'SQL_Latin1_General_CP1_CI_AS'
maxSizeBytes: 1073741824
}
}

resource appServicePlan 'Microsoft.Web/serverfarms@2022-09-01' = {
name: appServicePlanName
location: location
tags: tags
sku: {
name: 'B1'
capacity: 1
}
}

resource appService 'Microsoft.Web/sites@2022-09-01' = {
name: appServiceName
location: location
tags: union(tags, {
'azd-service-name': 'backend'
'hidden-related:${appServicePlan.id}': 'empty'
})
properties: {
httpsOnly: true
serverFarmId: appServicePlan.id
siteConfig: {
ftpsState: 'Disabled'
minTlsVersion: '1.2'
}
}

resource configLogs 'config' = {
name: 'logs'
properties: {
applicationLogs: { fileSystem: { level: 'Verbose' } }
detailedErrorMessages: { enabled: true }
failedRequestsTracing: { enabled: true }
httpLogs: { fileSystem: { retentionInMb: 35, retentionInDays: 3, enabled: true } }
}
}

resource connectionStrings 'config' = {
name: 'connectionstrings'
properties: {
DefaultConnection: {
value: 'Data Source=tcp:${sqlServer.properties.fullyQualifiedDomainName},1433;Initial Catalog=${sqlDatabase.name};User Id=${sqlServer.properties.administratorLogin};Password=${sqlAdminPassword};'
type: 'SQLAzure'
}
}
}
}

output SERVICE_ENDPOINT string = 'https://${appService.properties.defaultHostName}'
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using CommunityToolkit.Datasync.Server;
using CommunityToolkit.Datasync.Server.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc;
using Sample.Datasync.Server.Db;

namespace Sample.Datasync.Server.Controllers;

[Route("tables/[controller]")]
public class TodoItemController : TableController<TodoItem>
{
public TodoItemController(AppDbContext context)
: base(new EntityTableRepository<TodoItem>(context))
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.EntityFrameworkCore;

namespace Sample.Datasync.Server.Db;

public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{

}

public DbSet<TodoItem> TodoItems => Set<TodoItem>();

public async Task InitializeDatabaseAsync()
{
await Database.EnsureCreatedAsync();
}
}
16 changes: 16 additions & 0 deletions samples/datasync-server/src/Sample.Datasync.Server/Db/TodoItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using CommunityToolkit.Datasync.Server.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;

namespace Sample.Datasync.Server.Db;

public class TodoItem : EntityTableData
{
[Required, MinLength(1)]
public string Text { get; set; } = string.Empty;

public bool IsComplete { get; set; }
}
31 changes: 31 additions & 0 deletions samples/datasync-server/src/Sample.Datasync.Server/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using CommunityToolkit.Datasync.Server;
using Microsoft.EntityFrameworkCore;
using Sample.Datasync.Server.Db;

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

string connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new ApplicationException("DefaultConnection is not set");

builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(connectionString));
builder.Services.AddDatasyncServices();
builder.Services.AddControllers();

WebApplication app = builder.Build();

// Initialize the database
using (IServiceScope scope = app.Services.CreateScope())
{
AppDbContext context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await context.InitializeDatabaseAsync();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# These files are used during the Publish process from VS
PublishProfiles/
ServiceDependencies/
serviceDependencies.*
Loading

0 comments on commit a2f582c

Please sign in to comment.