Skip to content

Commit 96a9bd9

Browse files
committed
v2
1 parent 4b72af2 commit 96a9bd9

File tree

13 files changed

+135
-5
lines changed

13 files changed

+135
-5
lines changed

Api/src/UrlShortener.Core/Error.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace UrlShortener.Core;
2+
3+
public record Error(string Code, string Description)
4+
{
5+
public static Error None => new(string.Empty, string.Empty);
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace UrlShortener.Core;
2+
3+
public static class Errors
4+
{
5+
public static Error MissingCreatedBy => new("missing_value", "Created by is required");
6+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
namespace UrlShortener.Core;
2+
3+
public class Result<TValue>
4+
{
5+
private readonly TValue? _value;
6+
private readonly Error _error;
7+
8+
private readonly bool _isSuccess;
9+
10+
private Result(TValue value)
11+
{
12+
_isSuccess = true;
13+
_value = value;
14+
_error = Error.None;
15+
}
16+
17+
private Result(Error error)
18+
{
19+
_isSuccess = false;
20+
_value = default;
21+
_error = error;
22+
}
23+
24+
public bool Succeeded => _isSuccess;
25+
public TValue? Value => _value;
26+
public Error Error => _error;
27+
28+
public static Result<TValue> Success(TValue value) => new(value);
29+
public static Result<TValue> Failure(Error error) => new(error);
30+
31+
public static implicit operator Result<TValue>(TValue value) => new(value);
32+
public static implicit operator Result<TValue>(Error error) => new(error);
33+
34+
public TResult Match<TResult>(
35+
Func<TValue, TResult> success,
36+
Func<Error, TResult> failure)
37+
{
38+
return _isSuccess ? success(_value!) : failure(_error);
39+
}
40+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using UrlShortener.Api.Core.Tests;
2+
3+
namespace UrlShortener.Core.Urls.Add;
4+
5+
public class AddUrlHandler
6+
{
7+
private readonly ShortUrlGenerator _shortUrlGenerator;
8+
private readonly IUrlDataStore _urlDataStore;
9+
private readonly TimeProvider _timeProvider;
10+
11+
public AddUrlHandler(ShortUrlGenerator shortUrlGenerator, IUrlDataStore urlDataStore,
12+
TimeProvider timeProvider)
13+
{
14+
_shortUrlGenerator = shortUrlGenerator;
15+
_urlDataStore = urlDataStore;
16+
_timeProvider = timeProvider;
17+
}
18+
19+
public async Task<Result<AddUrlResponse>> HandleAsync(AddUrlRequest request, CancellationToken cancellationToken)
20+
{
21+
if(string.IsNullOrEmpty(request.CreatedBy))
22+
return Errors.MissingCreatedBy;
23+
24+
var shortened = new ShortenedUrl(request.LongUrl,
25+
_shortUrlGenerator.GenerateUniqueUrl(),
26+
request.CreatedBy,
27+
_timeProvider.GetUtcNow());
28+
29+
await _urlDataStore.AddAsync(shortened, cancellationToken);
30+
31+
return new AddUrlResponse(request.LongUrl, shortened.ShortUrl);
32+
}
33+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
namespace UrlShortener.Core.Urls.Add;
2+
3+
public record AddUrlRequest(Uri LongUrl,string CreatedBy);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
namespace UrlShortener.Core.Urls.Add;
2+
3+
public record AddUrlResponse(Uri LongUrl,string ShortUrl);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace UrlShortener.Core.Urls.Add;
2+
3+
public interface IUrlDataStore
4+
{
5+
Task AddAsync(ShortenedUrl shortened, CancellationToken cancellationToken);
6+
}

Api/src/UrlShortener.Core/ShortUrlGenerator.cs renamed to Api/src/UrlShortener.Core/Urls/Add/ShortUrlGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public ShortUrlGenerator(TokenProvider tokenProvider)
1111
_tokenProvider = tokenProvider;
1212
}
1313

14-
public string GenerateShortUrl()
14+
public string GenerateUniqueUrl()
1515
{
1616
return _tokenProvider
1717
.GetToken()
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace UrlShortener.Core.Urls;
2+
3+
public class ShortenedUrl
4+
{
5+
public ShortenedUrl(Uri longUrl, string shortUrl, string createdBy,
6+
DateTimeOffset createdOn)
7+
{
8+
LongUrl = longUrl;
9+
ShortUrl = shortUrl;
10+
CreatedBy = createdBy;
11+
CreatedOn = createdOn;
12+
}
13+
14+
public Uri LongUrl { get;}
15+
public string ShortUrl { get; }
16+
public string CreatedBy { get; }
17+
public DateTimeOffset CreatedOn { get; }
18+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using UrlShortener.Core.Urls;
2+
using UrlShortener.Core.Urls.Add;
3+
4+
namespace UrlShortener.Api.Core.Tests.TestDoubles;
5+
6+
public class InMemoryUrlDataStore :Dictionary<string,ShortenedUrl> ,IUrlDataStore
7+
{
8+
public async Task AddAsync(ShortenedUrl shortened, CancellationToken cancellationToken)
9+
{
10+
Add(shortened.ShortUrl,shortened);
11+
await Task.CompletedTask;
12+
}
13+
}

0 commit comments

Comments
 (0)