diff options
author | crupest <crupest@outlook.com> | 2020-02-02 14:35:30 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2020-02-02 14:35:30 +0800 |
commit | cb18564275735da0614be39371e2e3e7f5467e88 (patch) | |
tree | 06388f533584b06a185c5908603423af158f671a | |
parent | 9e84b1e9ad1f2a45cd3e09759c69989fdc588c3d (diff) | |
download | timeline-cb18564275735da0614be39371e2e3e7f5467e88.tar.gz timeline-cb18564275735da0614be39371e2e3e7f5467e88.tar.bz2 timeline-cb18564275735da0614be39371e2e3e7f5467e88.zip |
Add timeline service.
-rw-r--r-- | Timeline/Entities/TimelineEntity.cs | 4 | ||||
-rw-r--r-- | Timeline/Models/Validation/NameValidator.cs | 33 | ||||
-rw-r--r-- | Timeline/Models/Validation/TimelineNameValidator.cs | 19 | ||||
-rw-r--r-- | Timeline/Models/Validation/UsernameValidator.cs | 28 | ||||
-rw-r--r-- | Timeline/Resources/Models/Validation/NameValidator.Designer.cs (renamed from Timeline/Resources/Models/Validation/UsernameValidator.Designer.cs) | 6 | ||||
-rw-r--r-- | Timeline/Resources/Models/Validation/NameValidator.resx (renamed from Timeline/Resources/Models/Validation/UsernameValidator.resx) | 0 | ||||
-rw-r--r-- | Timeline/Resources/Services/TimelineService.Designer.cs | 18 | ||||
-rw-r--r-- | Timeline/Resources/Services/TimelineService.resx | 6 | ||||
-rw-r--r-- | Timeline/Services/TimelineService.cs | 96 | ||||
-rw-r--r-- | Timeline/Timeline.csproj | 8 |
10 files changed, 172 insertions, 46 deletions
diff --git a/Timeline/Entities/TimelineEntity.cs b/Timeline/Entities/TimelineEntity.cs index c50fe6dd..0b252bab 100644 --- a/Timeline/Entities/TimelineEntity.cs +++ b/Timeline/Entities/TimelineEntity.cs @@ -6,7 +6,8 @@ using Timeline.Models.Http; namespace Timeline.Entities
{
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "This is entity object.")]
+#pragma warning disable CA2227 // Collection properties should be read only
+ // TODO: Create index for this table.
[Table("timelines")]
public class TimelineEntity
{
@@ -38,4 +39,5 @@ namespace Timeline.Entities public List<TimelinePostEntity> Posts { get; set; } = default!;
}
+#pragma warning restore CA2227 // Collection properties should be read only
}
diff --git a/Timeline/Models/Validation/NameValidator.cs b/Timeline/Models/Validation/NameValidator.cs new file mode 100644 index 00000000..8db10ebd --- /dev/null +++ b/Timeline/Models/Validation/NameValidator.cs @@ -0,0 +1,33 @@ +using System.Linq;
+using static Timeline.Resources.Models.Validation.NameValidator;
+
+namespace Timeline.Models.Validation
+{
+ public class NameValidator : Validator<string>
+ {
+ public const int MaxLength = 26;
+
+ protected override (bool, string) DoValidate(string value)
+ {
+ if (value.Length == 0)
+ {
+ return (false, MessageEmptyString);
+ }
+
+ if (value.Length > 26)
+ {
+ return (false, MessageTooLong);
+ }
+
+ foreach ((char c, int i) in value.Select((c, i) => (c, i)))
+ {
+ if (!(char.IsLetterOrDigit(c) || c == '-' || c == '_'))
+ {
+ return (false, MessageInvalidChar);
+ }
+ }
+
+ return (true, GetSuccessMessage());
+ }
+ }
+}
diff --git a/Timeline/Models/Validation/TimelineNameValidator.cs b/Timeline/Models/Validation/TimelineNameValidator.cs new file mode 100644 index 00000000..f1ab54e8 --- /dev/null +++ b/Timeline/Models/Validation/TimelineNameValidator.cs @@ -0,0 +1,19 @@ +using System;
+
+namespace Timeline.Models.Validation
+{
+ public class TimelineNameValidator : NameValidator
+ {
+ }
+
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
+ AllowMultiple = false)]
+ public class TimelineNameAttribute : ValidateWithAttribute
+ {
+ public TimelineNameAttribute()
+ : base(typeof(TimelineNameValidator))
+ {
+
+ }
+ }
+}
diff --git a/Timeline/Models/Validation/UsernameValidator.cs b/Timeline/Models/Validation/UsernameValidator.cs index d8f3bdc0..87bbf85f 100644 --- a/Timeline/Models/Validation/UsernameValidator.cs +++ b/Timeline/Models/Validation/UsernameValidator.cs @@ -1,35 +1,9 @@ using System;
-using System.Linq;
-using static Timeline.Resources.Models.Validation.UsernameValidator;
namespace Timeline.Models.Validation
{
- public class UsernameValidator : Validator<string>
+ public class UsernameValidator : NameValidator
{
- public const int MaxLength = 26;
-
- protected override (bool, string) DoValidate(string value)
- {
- if (value.Length == 0)
- {
- return (false, MessageEmptyString);
- }
-
- if (value.Length > 26)
- {
- return (false, MessageTooLong);
- }
-
- foreach ((char c, int i) in value.Select((c, i) => (c, i)))
- {
- if (!(char.IsLetterOrDigit(c) || c == '-' || c == '_'))
- {
- return (false, MessageInvalidChar);
- }
- }
-
- return (true, GetSuccessMessage());
- }
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
diff --git a/Timeline/Resources/Models/Validation/UsernameValidator.Designer.cs b/Timeline/Resources/Models/Validation/NameValidator.Designer.cs index ac925504..5b869226 100644 --- a/Timeline/Resources/Models/Validation/UsernameValidator.Designer.cs +++ b/Timeline/Resources/Models/Validation/NameValidator.Designer.cs @@ -22,14 +22,14 @@ namespace Timeline.Resources.Models.Validation { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class UsernameValidator {
+ internal class NameValidator {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal UsernameValidator() {
+ internal NameValidator() {
}
/// <summary>
@@ -39,7 +39,7 @@ namespace Timeline.Resources.Models.Validation { internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Models.Validation.UsernameValidator", typeof(UsernameValidator).Assembly);
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Models.Validation.NameValidator", typeof(NameValidator).Assembly);
resourceMan = temp;
}
return resourceMan;
diff --git a/Timeline/Resources/Models/Validation/UsernameValidator.resx b/Timeline/Resources/Models/Validation/NameValidator.resx index 08a814d0..08a814d0 100644 --- a/Timeline/Resources/Models/Validation/UsernameValidator.resx +++ b/Timeline/Resources/Models/Validation/NameValidator.resx diff --git a/Timeline/Resources/Services/TimelineService.Designer.cs b/Timeline/Resources/Services/TimelineService.Designer.cs index 8212c252..3ee5959f 100644 --- a/Timeline/Resources/Services/TimelineService.Designer.cs +++ b/Timeline/Resources/Services/TimelineService.Designer.cs @@ -77,5 +77,23 @@ namespace Timeline.Resources.Services { return ResourceManager.GetString("ExceptionFindTimelineUsernameBadFormat", resourceCulture);
}
}
+
+ /// <summary>
+ /// Looks up a localized string similar to The timeline name is of bad format because {0}..
+ /// </summary>
+ internal static string ExceptionTimelineNameBadFormat {
+ get {
+ return ResourceManager.GetString("ExceptionTimelineNameBadFormat", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The timeline with given name already exists..
+ /// </summary>
+ internal static string ExceptionTimelineNameConflict {
+ get {
+ return ResourceManager.GetString("ExceptionTimelineNameConflict", resourceCulture);
+ }
+ }
}
}
diff --git a/Timeline/Resources/Services/TimelineService.resx b/Timeline/Resources/Services/TimelineService.resx index 0429a2f8..e0d76c9a 100644 --- a/Timeline/Resources/Services/TimelineService.resx +++ b/Timeline/Resources/Services/TimelineService.resx @@ -123,4 +123,10 @@ <data name="ExceptionFindTimelineUsernameBadFormat" xml:space="preserve">
<value>The owner username of personal timeline is of bad format.</value>
</data>
+ <data name="ExceptionTimelineNameBadFormat" xml:space="preserve">
+ <value>The timeline name is of bad format because {0}.</value>
+ </data>
+ <data name="ExceptionTimelineNameConflict" xml:space="preserve">
+ <value>The timeline with given name already exists.</value>
+ </data>
</root>
\ No newline at end of file diff --git a/Timeline/Services/TimelineService.cs b/Timeline/Services/TimelineService.cs index a16237ca..b031297e 100644 --- a/Timeline/Services/TimelineService.cs +++ b/Timeline/Services/TimelineService.cs @@ -213,11 +213,12 @@ namespace Timeline.Services /// </summary>
/// <param name="name">The name of the timeline.</param>
/// <param name="owner">The id of owner of the timeline.</param>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="name"/> or <paramref name="owner"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when timeline name is invalid. Currently it means it is an empty string.</exception>
+ /// <returns>The info of the new timeline.</returns>
+ /// <exception cref="ArgumentNullException">Thrown when <paramref name="name"/> is null.</exception>
+ /// <exception cref="ArgumentException">Thrown when timeline name is invalid.</exception>
/// <exception cref="ConflictException">Thrown when the timeline already exists.</exception>
/// <exception cref="UserNotExistException">Thrown when the owner user does not exist.</exception>
- Task CreateTimeline(string name, long owner);
+ Task<TimelineInfo> CreateTimeline(string name, long owner);
}
public interface IPersonalTimelineService : IBaseTimelineService
@@ -245,6 +246,17 @@ namespace Timeline.Services protected IMapper Mapper { get; }
+ protected TimelineEntity CreateNewEntity(string? name, long owner)
+ {
+ return new TimelineEntity
+ {
+ Name = name,
+ OwnerId = owner,
+ Visibility = TimelineVisibility.Register,
+ CreateTime = Clock.GetCurrentTime()
+ };
+ }
+
/// <summary>
/// Find the timeline id by the name.
/// For details, see remarks.
@@ -537,6 +549,72 @@ namespace Timeline.Services }
}
+ public class TimelineService : BaseTimelineService, ITimelineService
+ {
+ private readonly TimelineNameValidator _timelineNameValidator = new TimelineNameValidator();
+
+ private void ValidateTimelineName(string name, string paramName)
+ {
+ if (!_timelineNameValidator.Validate(name, out var message))
+ {
+ throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExceptionTimelineNameBadFormat, message), paramName);
+ }
+ }
+
+ public TimelineService(ILoggerFactory loggerFactory, DatabaseContext database, IUserService userService, IMapper mapper, IClock clock)
+ : base(loggerFactory, database, userService, mapper, clock)
+ {
+
+ }
+
+ protected override async Task<long> FindTimelineId(string name)
+ {
+ if (name == null)
+ throw new ArgumentNullException(nameof(name));
+
+ ValidateTimelineName(name, nameof(name));
+
+ var timelineEntity = await Database.Timelines.Where(t => t.Name == name).Select(t => new { t.Id }).SingleOrDefaultAsync();
+
+ if (timelineEntity == null)
+ {
+ throw new TimelineNotExistException(name);
+ }
+ else
+ {
+ return timelineEntity.Id;
+ }
+ }
+
+ public async Task<TimelineInfo> CreateTimeline(string name, long owner)
+ {
+ if (name == null)
+ throw new ArgumentNullException(nameof(name));
+
+ ValidateTimelineName(name, nameof(name));
+
+ var user = await UserService.GetUserById(owner);
+
+ var conflict = await Database.Timelines.AnyAsync(t => t.Name == name);
+
+ if (conflict)
+ throw new ConflictException(ExceptionTimelineNameConflict);
+
+ var newEntity = CreateNewEntity(name, owner);
+ Database.Timelines.Add(newEntity);
+ await Database.SaveChangesAsync();
+
+ return new TimelineInfo
+ {
+ Name = name,
+ Description = "",
+ Owner = Mapper.Map<UserInfo>(user),
+ Visibility = newEntity.Visibility,
+ Members = new List<UserInfo>()
+ };
+ }
+ }
+
public class PersonalTimelineService : BaseTimelineService, IPersonalTimelineService
{
public PersonalTimelineService(ILoggerFactory loggerFactory, DatabaseContext database, IUserService userService, IMapper mapper, IClock clock)
@@ -547,6 +625,9 @@ namespace Timeline.Services protected override async Task<long> FindTimelineId(string name)
{
+ if (name == null)
+ throw new ArgumentNullException(nameof(name));
+
long userId;
try
{
@@ -569,14 +650,7 @@ namespace Timeline.Services }
else
{
- var newTimelineEntity = new TimelineEntity
- {
- Name = null,
- Description = null,
- OwnerId = userId,
- Visibility = TimelineVisibility.Register,
- CreateTime = Clock.GetCurrentTime(),
- };
+ var newTimelineEntity = CreateNewEntity(null, userId);
Database.Timelines.Add(newTimelineEntity);
await Database.SaveChangesAsync();
diff --git a/Timeline/Timeline.csproj b/Timeline/Timeline.csproj index 1a3a07cd..08999f82 100644 --- a/Timeline/Timeline.csproj +++ b/Timeline/Timeline.csproj @@ -89,10 +89,10 @@ <AutoGen>True</AutoGen>
<DependentUpon>NicknameValidator.resx</DependentUpon>
</Compile>
- <Compile Update="Resources\Models\Validation\UsernameValidator.Designer.cs">
+ <Compile Update="Resources\Models\Validation\NameValidator.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
- <DependentUpon>UsernameValidator.resx</DependentUpon>
+ <DependentUpon>NameValidator.resx</DependentUpon>
</Compile>
<Compile Update="Resources\Models\Validation\Validator.Designer.cs">
<DesignTime>True</DesignTime>
@@ -163,9 +163,9 @@ <Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>NicknameValidator.Designer.cs</LastGenOutput>
</EmbeddedResource>
- <EmbeddedResource Update="Resources\Models\Validation\UsernameValidator.resx">
+ <EmbeddedResource Update="Resources\Models\Validation\NameValidator.resx">
<Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>UsernameValidator.Designer.cs</LastGenOutput>
+ <LastGenOutput>NameValidator.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Resources\Models\Validation\Validator.resx">
<Generator>ResXFileCodeGenerator</Generator>
|