Skip to content

Instantly share code, notes, and snippets.

@Kikimora
Created July 27, 2017 21:49
Show Gist options
  • Save Kikimora/ef1715db5dbb41554cf6ea974894a8ff to your computer and use it in GitHub Desktop.
Save Kikimora/ef1715db5dbb41554cf6ea974894a8ff to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using Avend.API.Infrastructure;
using Avend.API.Infrastructure.Validation;
using Avend.API.Model;
using Avend.API.Services.Profile;
using Avend.API.Validation.Util;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Qoden.Validation;
namespace Avend.API.Services.Subscriptions
{
[DataContract]
public class UserInviteRequestDto
{
[DataMember(Name = "first_name")]
public string FirstName { get; set; }
[DataMember(Name = "last_name")]
public string LastName { get; set; }
[DataMember(Name = "email")]
public string Email { get; set; }
[DataMember(Name = "text")]
public string InviteText { get; set; }
public static UserInviteRequestDto From(SubscriptionInvite invite)
{
return new UserInviteRequestDto
{
FirstName = invite.FirstName,
LastName = invite.LastName,
Email = invite.Email,
InviteText = invite.InviteText
};
}
}
public class InviteService
{
private readonly DbContextOptions<AvendDbContext> _dbOptions;
private readonly ISendGrid _sendgrid;
private readonly UserContext _userContext;
private readonly EmailTemplateRecord _inviteTemplate;
private readonly IConfiguration _config;
public InviteService(
DbContextOptions<AvendDbContext> dbOptions,
UserContext userContext,
ISendGrid sendgrid,
IConfiguration config)
{
Assert.Argument(dbOptions, nameof(dbOptions)).NotNull();
Assert.Argument(userContext, nameof(userContext)).NotNull();
Assert.Argument(sendgrid, nameof(sendgrid)).NotNull();
Assert.Argument(config, nameof(config)).NotNull();
_dbOptions = dbOptions;
_sendgrid = sendgrid;
_userContext = userContext;
_config = config;
_inviteTemplate = new EmailTemplateRecord()
{
Subject = _config.GetSection("InviteEmail:Subject").Value,
Message = new Dictionary<string, string>()
{
{"text/plain", _config.GetSection("InviteEmail:text/plain").Value},
{"text/html", _config.GetSection("InviteEmail:text/html").Value},
}
};
}
public async Task AcceptInvite(Guid userUid, string inviteCode)
{
Assert.Argument(inviteCode, nameof(inviteCode)).NotNull();
using (var db = new AvendDbContext(_dbOptions))
{
var member = new SubscriptionMember(null, db);
await member.Load(userUid);
member.AssertValidMember();
var invite = member.AcceptInvite(inviteCode);
Check.Value(invite, "invite", AvendErrors.NotFound).NotNull("Invite not found");
await db.SaveChangesAsync();
//this feels like a hack but I don't see any other good way around (averbin)
_userContext.Subscription = SubscriptionDto.From(invite.Subscription);
_userContext.Tenant = TenantDto.From(invite.Subscription);
}
}
public async Task<SubscriptionMemberDto> InviteUser(UserInviteRequestDto inviteRequest, Guid? subscriptionUid)
{
if (_userContext.Role == UserRole.SuperAdmin)
{
Check.Value(subscriptionUid).HasValue("Super Admin must supply subscription uid");
}
else if (_userContext.Role == UserRole.Admin)
{
subscriptionUid = _userContext.Subscription.Uid.GetValueOrDefault();
}
else
{
var error = new Error("Not found");
error.NotFound();
throw new ErrorException(error);
}
var validator = new Validator();
validator.CheckDataMember(inviteRequest, x => x.Email).IsEmail();
validator.CheckDataMember(inviteRequest, x => x.FirstName).IsShortText();
validator.CheckDataMember(inviteRequest, x => x.LastName).IsShortText();
inviteRequest.InviteText = inviteRequest.InviteText ?? string.Empty;
validator.Throw();
using (var db = new AvendDbContext(_dbOptions))
{
var admin = new SubscriptionAdmin(_userContext, db);
admin.ManageSubscription(subscriptionUid.GetValueOrDefault());
var invite = admin.Invite(inviteRequest);
await SendInvite(invite, db);
return SubscriptionMemberDto.From(invite);
}
}
public async Task<UserInviteRequestDto> GetInvite(string inviteCode)
{
using (var db = new AvendDbContext(_dbOptions))
{
var repo = new SubscriptionMemberRepository(db);
var invite = await repo.FindInviteByIdAsync(inviteCode);
Check.Value(invite, onError: AvendErrors.NotFound).NotNull("Not found");
Check.Value(invite.Accepted, onError: AvendErrors.NotFound).IsFalse("Invite already accepted");
return UserInviteRequestDto.From(invite);
}
}
public async Task<SubscriptionMemberDto> ResendInvite(Guid uid)
{
var rateLimit = _config.GetSection("RateLimit:ResendInvite").Get<float>();
using (var db = new AvendDbContext(_dbOptions))
{
var admin = new SubscriptionAdmin(_userContext, db);
var invite = admin.ReInvite(uid);
Check.Value(invite, onError: AvendErrors.NotFound).NotNull("Invite not found");
var sentLastTime = invite.UpdatedAt.GetValueOrDefault(invite.CreatedAt);
var nextInvite = sentLastTime + TimeSpan.FromSeconds(rateLimit);
Check.Value(nextInvite)
.Less(DateTime.Now.ToUniversalTime(), "Cannot resend invite. Please wait few minutes.",
AvendErrors.RateLimit);
await SendInvite(invite, db);
return SubscriptionMemberDto.From(invite);
}
}
private async Task SendInvite(SubscriptionInvite invite, AvendDbContext db)
{
var message = new SendGridMessage()
{
Email = invite.Email,
Personalization = new Qoden.Reflection.Record(invite)
};
var sendResult = await _sendgrid.Send(_inviteTemplate.Subject, _inviteTemplate.Message, new[] {message});
Check.Value(sendResult).IsTrue("Invite email send failed");
await db.SaveChangesAsync();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment