Skip to content

Commit 91a73af

Browse files
committed
v3
1 parent 660926a commit 91a73af

File tree

5 files changed

+189
-0
lines changed

5 files changed

+189
-0
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using Microsoft.Azure.Cosmos;
2+
using Newtonsoft.Json;
3+
using UrlShortener.Core.Urls;
4+
using UrlShortener.Core.Urls.Add;
5+
6+
namespace UrlShortener.Infrastructure;
7+
8+
public class CosmosDbUrlDataStore : IUrlDataStore
9+
{
10+
private readonly Container _container;
11+
12+
public CosmosDbUrlDataStore(Container container)
13+
{
14+
_container = container;
15+
}
16+
17+
public async Task AddAsync(ShortenedUrl shortened, CancellationToken cancellationToken)
18+
{
19+
var document = (ShortenedUrlCosmos)shortened;
20+
await _container.CreateItemAsync(document,
21+
new PartitionKey(document.PartitionKey),
22+
cancellationToken: cancellationToken);
23+
}
24+
25+
internal class ShortenedUrlCosmos
26+
{
27+
public string LongUrl { get; }
28+
29+
[JsonProperty(PropertyName = "id")] // Cosmos DB Unique Identifier
30+
public string ShortUrl { get; }
31+
32+
public DateTimeOffset CreatedOn { get; }
33+
public string CreatedBy { get; }
34+
35+
public string PartitionKey => ShortUrl[..1]; // Cosmos DB Partition Key
36+
37+
public ShortenedUrlCosmos(string longUrl, string shortUrl, string createdBy, DateTimeOffset createdOn)
38+
{
39+
LongUrl = longUrl;
40+
ShortUrl = shortUrl;
41+
CreatedOn = createdOn;
42+
CreatedBy = createdBy;
43+
}
44+
45+
public static implicit operator ShortenedUrl(ShortenedUrlCosmos url) =>
46+
new(new Uri(url.LongUrl), url.ShortUrl, url.CreatedBy, url.CreatedOn);
47+
48+
public static explicit operator ShortenedUrlCosmos(ShortenedUrl url) =>
49+
new(url.LongUrl.ToString(), url.ShortUrl, url.CreatedBy, url.CreatedOn);
50+
}
51+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using Castle.Core.Configuration;
2+
using Microsoft.Azure.Cosmos;
3+
using Microsoft.Extensions.DependencyInjection;
4+
using UrlShortener.Core.Urls.Add;
5+
6+
namespace UrlShortener.Infrastructure.Extensions;
7+
8+
public static class ServiceCollectionExtensions
9+
{
10+
public static IServiceCollection AddCosmosUrlDataStore(
11+
this IServiceCollection services,
12+
IConfiguration configuration)
13+
{
14+
services.AddSingleton<CosmosClient>(s =>
15+
new CosmosClient(configuration["CosmosDb:ConnectionString"]!));
16+
17+
services.AddSingleton<IUrlDataStore>(s =>
18+
{
19+
var cosmosClient = s.GetRequiredService<CosmosClient>();
20+
21+
var container =
22+
cosmosClient.GetContainer(
23+
configuration["DatabaseName"]!,
24+
configuration["ContainerName"]!);
25+
26+
return new CosmosDbUrlDataStore(container);
27+
});
28+
29+
return services;
30+
}
31+
32+
}

GenerateUrlShortener.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UrlShortener.Core", "Api\sr
1717
EndProject
1818
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UrlShortener.Tests", "Api\tests\UrlShortener.Tests\UrlShortener.Tests.csproj", "{13087227-6485-4CF6-80E7-E08D7DA10D9F}"
1919
EndProject
20+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UrlShortener.Infrastructure", "Api\src\UrlShortener.Infrastructure\UrlShortener.Infrastructure.csproj", "{31E415BE-7384-42C6-BB72-323485EF5A7E}"
21+
EndProject
2022
Global
2123
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2224
Debug|Any CPU = Debug|Any CPU
@@ -42,6 +44,10 @@ Global
4244
{13087227-6485-4CF6-80E7-E08D7DA10D9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
4345
{13087227-6485-4CF6-80E7-E08D7DA10D9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
4446
{13087227-6485-4CF6-80E7-E08D7DA10D9F}.Release|Any CPU.Build.0 = Release|Any CPU
47+
{31E415BE-7384-42C6-BB72-323485EF5A7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
48+
{31E415BE-7384-42C6-BB72-323485EF5A7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
49+
{31E415BE-7384-42C6-BB72-323485EF5A7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
50+
{31E415BE-7384-42C6-BB72-323485EF5A7E}.Release|Any CPU.Build.0 = Release|Any CPU
4551
EndGlobalSection
4652
GlobalSection(NestedProjects) = preSolution
4753
{E8A1DDA7-C059-4F2A-BD69-FB182403494E} = {25BDC746-EDEE-4BF8-B0B1-C591F3675800}
@@ -50,5 +56,6 @@ Global
5056
{1CDF3857-E332-4E13-8CCB-FD03275AB935} = {22B1E0E4-6E75-4AEE-A9BC-60A443602471}
5157
{F9653843-B56F-4384-9A90-F65984099016} = {E8A1DDA7-C059-4F2A-BD69-FB182403494E}
5258
{13087227-6485-4CF6-80E7-E08D7DA10D9F} = {22B1E0E4-6E75-4AEE-A9BC-60A443602471}
59+
{31E415BE-7384-42C6-BB72-323485EF5A7E} = {E8A1DDA7-C059-4F2A-BD69-FB182403494E}
5360
EndGlobalSection
5461
EndGlobal

infrastructure/main.bicep

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,19 @@ module keyVault 'modules/secrets/keyvault.bicep' = {
1010
}
1111
}
1212

13+
module cosmosDb 'modules/storage/cosmos-db.bicep' = {
14+
name: 'cosmosDbDeployment'
15+
params: {
16+
name: 'cosmos-db-${uniqueId}'
17+
location: location
18+
kind: 'GlobalDocumentDB'
19+
databaseName: 'urls'
20+
locationName: 'Spain Central'
21+
keyVaultName: keyVault.outputs.name
22+
}
23+
24+
}
25+
1326
module apiService 'modules/compute/appservice.bicep' = {
1427
name: 'apiDeployment'
1528
params: {
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
param name string
2+
param location string
3+
param kind string
4+
param databaseName string
5+
param locationName string
6+
param keyVaultName string
7+
8+
param containers array = [
9+
{
10+
name: 'items'
11+
partitionKey: '/PartitionKey'
12+
}
13+
]
14+
15+
resource cosmosDbAccount 'Microsoft.DocumentDB/databaseAccounts@2024-05-15' = {
16+
name: name
17+
location: location
18+
kind: kind
19+
properties: {
20+
databaseAccountOfferType: 'Standard'
21+
locations: [
22+
{
23+
locationName: locationName
24+
failoverPriority: 0
25+
isZoneRedundant: false
26+
}
27+
]
28+
}
29+
}
30+
31+
resource cosmosDbDatabase 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2024-05-15' = {
32+
parent: cosmosDbAccount
33+
name: databaseName
34+
properties: {
35+
resource: {
36+
id: databaseName
37+
}
38+
}
39+
}
40+
41+
resource cosmosDbContainers 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2024-05-15' = [
42+
for container in containers: {
43+
parent: cosmosDbDatabase
44+
name: container.name
45+
properties: {
46+
resource: {
47+
id: container.name
48+
partitionKey: {
49+
paths: [
50+
container.partitionKey
51+
]
52+
kind: 'Hash'
53+
}
54+
indexingPolicy: {
55+
automatic: true
56+
indexingMode: 'consistent'
57+
includedPaths: [
58+
{
59+
path: '/*'
60+
}
61+
]
62+
excludedPaths: [
63+
{
64+
path: '/"_etag"/?'
65+
}
66+
]
67+
}
68+
defaultTtl: -1
69+
}
70+
}
71+
}
72+
]
73+
74+
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = {
75+
name: keyVaultName
76+
}
77+
78+
resource cosmosDbConnectionString 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = {
79+
parent: keyVault
80+
name: 'CosmosDb--ConnectionString'
81+
properties: {
82+
value: cosmosDbAccount.listConnectionStrings().connectionStrings[0].connectionString
83+
}
84+
}
85+
86+
output cosmosDbId string = cosmosDbAccount.id

0 commit comments

Comments
 (0)