Browse Source

Some refactoring

pull/1/head
Sebastian 10 years ago
parent
commit
43e6b097a1
  1. 4
      src/Squidex.Events/Apps/AppClientAttached.cs
  2. 2
      src/Squidex.Events/Apps/AppClientRenamed.cs
  3. 2
      src/Squidex.Events/Apps/AppClientRevoked.cs
  4. 9
      src/Squidex.Events/Apps/AppLanguageAdded.cs
  5. 19
      src/Squidex.Events/Apps/AppLanguageRemoved.cs
  6. 19
      src/Squidex.Events/Apps/AppMasterLanguageSet.cs
  7. 2
      src/Squidex.Infrastructure/DomainObjectDeletedException.cs
  8. 15
      src/Squidex.Infrastructure/DomainObjectException.cs
  9. 10
      src/Squidex.Infrastructure/DomainObjectNotFoundException.cs
  10. 10
      src/Squidex.Infrastructure/DomainObjectVersionException.cs
  11. 6
      src/Squidex.Read/Apps/IAppClientEntity.cs
  12. 2
      src/Squidex.Read/Apps/Services/Implementations/CachingAppProvider.cs
  13. 4
      src/Squidex.Store.MongoDb/Apps/MongoAppClientEntity.cs
  14. 14
      src/Squidex.Store.MongoDb/Apps/MongoAppRepository.cs
  15. 38
      src/Squidex.Write/Apps/AppClient.cs
  16. 79
      src/Squidex.Write/Apps/AppClients.cs
  17. 11
      src/Squidex.Write/Apps/AppCommandHandler.cs
  18. 77
      src/Squidex.Write/Apps/AppContributors.cs
  19. 165
      src/Squidex.Write/Apps/AppDomainObject.cs
  20. 83
      src/Squidex.Write/Apps/AppLanguages.cs
  21. 26
      src/Squidex.Write/Apps/Commands/AddLanguage.cs
  22. 6
      src/Squidex.Write/Apps/Commands/AttachClient.cs
  23. 26
      src/Squidex.Write/Apps/Commands/RemoveLanguage.cs
  24. 6
      src/Squidex.Write/Apps/Commands/RenameClient.cs
  25. 6
      src/Squidex.Write/Apps/Commands/RevokeClient.cs
  26. 26
      src/Squidex.Write/Apps/Commands/SetMasterLanguage.cs
  27. 4
      src/Squidex/Config/Identity/LazyClientStore.cs
  28. 10
      src/Squidex/Controllers/Api/Apps/AppClientsController.cs
  29. 2
      src/Squidex/Controllers/Api/Apps/Models/AttachClientDto.cs
  30. 12
      src/Squidex/Controllers/Api/Apps/Models/ClientDto.cs
  31. 2
      src/Squidex/Pipeline/AppFilterAttribute.cs
  32. 2
      src/Squidex/app/components/internal/app/settings/clients-page.component.html
  33. 4
      src/Squidex/app/components/internal/app/settings/clients-page.component.ts
  34. 21
      src/Squidex/app/shared/services/app-clients.service.spec.ts
  35. 13
      src/Squidex/app/shared/services/app-clients.service.ts
  36. 25
      tests/Squidex.Write.Tests/Apps/AppCommandHandlerTests.cs
  37. 91
      tests/Squidex.Write.Tests/Apps/AppDomainObjectTests.cs

4
src/Squidex.Events/Apps/AppClientAttached.cs

@ -15,9 +15,9 @@ namespace Squidex.Events.Apps
[TypeName("AppClientAttachedEvent")]
public sealed class AppClientAttached : IEvent
{
public string ClientId { get; set; }
public string Id { get; set; }
public string ClientSecret { get; set; }
public string Secret { get; set; }
public DateTime ExpiresUtc { get; set; }
}

2
src/Squidex.Events/Apps/AppClientRenamed.cs

@ -14,7 +14,7 @@ namespace Squidex.Events.Apps
[TypeName("AppClientRenamedEvent")]
public sealed class AppClientRenamed : IEvent
{
public string ClientId { get; set; }
public string Id { get; set; }
public string Name { get; set; }
}

2
src/Squidex.Events/Apps/AppClientRevoked.cs

@ -14,6 +14,6 @@ namespace Squidex.Events.Apps
[TypeName("AppClientRevokedEvent")]
public sealed class AppClientRevoked : IEvent
{
public string ClientId { get; set; }
public string Id { get; set; }
}
}

9
src/Squidex.Events/Apps/AppLanguagesConfigured.cs → src/Squidex.Events/Apps/AppLanguageAdded.cs

@ -1,20 +1,19 @@
// ==========================================================================
// AppLanguagesConfigured.cs
// AppLanguageAdded.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
namespace Squidex.Events.Apps
{
[TypeName("AppLanguagesConfiguredEvent")]
public sealed class AppLanguagesConfigured : IEvent
[TypeName("AppLanguageAddedEvent")]
public sealed class AppLanguageAdded : IEvent
{
public List<Language> Languages { get; set; }
public Language Language { get; set; }
}
}

19
src/Squidex.Events/Apps/AppLanguageRemoved.cs

@ -0,0 +1,19 @@
// ==========================================================================
// AppLanguageRemoved.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
namespace Squidex.Events.Apps
{
[TypeName("AppLanguageRemovedEvent")]
public sealed class AppLanguageRemoved : IEvent
{
public Language Language { get; set; }
}
}

19
src/Squidex.Events/Apps/AppMasterLanguageSet.cs

@ -0,0 +1,19 @@
// ==========================================================================
// AppMasterLanguageSet.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
namespace Squidex.Events.Apps
{
[TypeName("AppMasterLanguageSetEvent")]
public sealed class AppMasterLanguageSet : IEvent
{
public Language Language { get; set; }
}
}

2
src/Squidex.Infrastructure/DomainObjectDeletedException.cs

@ -19,7 +19,7 @@ namespace Squidex.Infrastructure
private static string FormatMessage(string id, Type type)
{
return $"Domain object \'{id}\' (type {type}) not deleted.";
return $"Domain object \'{id}\' (type {type}) already deleted.";
}
}
}

15
src/Squidex.Infrastructure/DomainObjectException.cs

@ -17,18 +17,12 @@ namespace Squidex.Infrastructure
public string TypeName
{
get
{
return typeName;
}
get { return typeName; }
}
public string Id
{
get
{
return id;
}
get { return id; }
}
protected DomainObjectException(string message, string id, Type type)
@ -41,10 +35,7 @@ namespace Squidex.Infrastructure
{
this.id = id;
if (type != null)
{
typeName = type.Name;
}
typeName = type?.Name;
}
}
}

10
src/Squidex.Infrastructure/DomainObjectNotFoundException.cs

@ -17,9 +17,19 @@ namespace Squidex.Infrastructure
{
}
public DomainObjectNotFoundException(string id, string collection, Type type)
: base(FormatMessage(id, collection, type), id, type)
{
}
private static string FormatMessage(string id, Type type)
{
return $"Domain object \'{id}\' (type {type}) not found.";
}
private static string FormatMessage(string id, string collection, Type type)
{
return $"Domain object \'{id}\' not found on {type}.{collection}";
}
}
}

10
src/Squidex.Infrastructure/DomainObjectVersionException.cs

@ -17,18 +17,12 @@ namespace Squidex.Infrastructure
public int CurrentVersion
{
get
{
return currentVersion;
}
get { return currentVersion; }
}
public int ExpectedVersion
{
get
{
return expectedVersion;
}
get { return expectedVersion; }
}
public DomainObjectVersionException(string id, Type type, int currentVersion, int expectedVersion)

6
src/Squidex.Read/Apps/IAppClientEntity.cs

@ -12,11 +12,11 @@ namespace Squidex.Read.Apps
{
public interface IAppClientEntity
{
string Name { get; }
string Id { get; }
string ClientId { get; }
string Name { get; }
string ClientSecret { get; }
string Secret { get; }
DateTime ExpiresUtc { get; }
}

2
src/Squidex.Read/Apps/Services/Implementations/CachingAppProvider.cs

@ -69,7 +69,7 @@ namespace Squidex.Read.Apps.Services.Implementations
@event.Payload is AppClientAttached ||
@event.Payload is AppClientRevoked ||
@event.Payload is AppClientRenamed ||
@event.Payload is AppLanguagesConfigured)
@event.Payload is AppLanguageAdded)
{
var appName = Cache.Get<string>(BuildNamesCacheKey(@event.Headers.AggregateId()));

4
src/Squidex.Store.MongoDb/Apps/MongoAppClientEntity.cs

@ -16,11 +16,11 @@ namespace Squidex.Store.MongoDb.Apps
{
[BsonRequired]
[BsonElement]
public string ClientId { get; set; }
public string Id { get; set; }
[BsonRequired]
[BsonElement]
public string ClientSecret { get; set; }
public string Secret { get; set; }
[BsonRequired]
[BsonElement]

14
src/Squidex.Store.MongoDb/Apps/MongoAppRepository.cs

@ -71,19 +71,11 @@ namespace Squidex.Store.MongoDb.Apps
});
}
protected Task On(AppLanguagesConfigured @event, EnvelopeHeaders headers)
{
return Collection.UpdateAsync(headers, a =>
{
a.Languages = @event.Languages.Select(x => x.Iso2Code).ToList();
});
}
protected Task On(AppClientAttached @event, EnvelopeHeaders headers)
{
return Collection.UpdateAsync(headers, a =>
{
a.Clients.Add(@event.ClientId, SimpleMapper.Map(@event, new MongoAppClientEntity()));
a.Clients.Add(@event.Id, SimpleMapper.Map(@event, new MongoAppClientEntity()));
});
}
@ -91,7 +83,7 @@ namespace Squidex.Store.MongoDb.Apps
{
return Collection.UpdateAsync(headers, a =>
{
a.Clients.Remove(@event.ClientId);
a.Clients.Remove(@event.Id);
});
}
@ -99,7 +91,7 @@ namespace Squidex.Store.MongoDb.Apps
{
return Collection.UpdateAsync(headers, a =>
{
a.Clients[@event.ClientId].Name = @event.Name;
a.Clients[@event.Id].Name = @event.Name;
});
}

38
src/Squidex.Write/Apps/AppClient.cs

@ -13,33 +13,45 @@ namespace Squidex.Write.Apps
{
public sealed class AppClient
{
private string name;
private readonly string name;
private readonly string id;
private readonly string secret;
private readonly DateTime expiresUtc;
public string ClientId { get; }
public string Id
{
get { return id; }
}
public string ClientSecret { get; }
public string Name
{
get { return name ?? Id; }
}
public DateTime ExpiresUtc { get; }
public string Secret
{
get { return secret; }
}
public string Name
public DateTime ExpiresUtc
{
get { return name ?? ClientId; }
get { return expiresUtc; }
}
public AppClient(string id, string secret, DateTime expiresUtc)
public AppClient(string id, string secret, DateTime expiresUtc, string name = null)
{
Guard.NotNullOrEmpty(id, nameof(id));
Guard.NotNullOrEmpty(secret, nameof(secret));
ClientId = id;
ClientSecret = secret;
ExpiresUtc = expiresUtc;
this.id = id;
this.name = name;
this.secret = secret;
this.expiresUtc = expiresUtc;
}
public void Rename(string newName)
public AppClient Rename(string newName)
{
name = newName;
return new AppClient(Id, Secret, ExpiresUtc, newName);
}
}
}

79
src/Squidex.Write/Apps/AppClients.cs

@ -0,0 +1,79 @@
// ==========================================================================
// AppClients.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Infrastructure;
// ReSharper disable InvertIf
namespace Squidex.Write.Apps
{
public class AppClients
{
private readonly Dictionary<string, AppClient> clients = new Dictionary<string, AppClient>();
public IReadOnlyDictionary<string, AppClient> Clients
{
get { return clients; }
}
public void Add(string id, string secret, DateTime expires)
{
Func<string> message = () => "Cannot add client";
ThrowIfFound(id, message);
clients[id] = new AppClient(id, secret, expires);
}
public void Rename(string clientId, string name)
{
Func<string> message = () => "Cannot rename client";
ThrowIfNotFound(clientId);
ThrowIfSameName(clientId, name, message);
clients[clientId] = clients[clientId].Rename(name);
}
public void Revoke(string clientId)
{
ThrowIfNotFound(clientId);
clients.Remove(clientId);
}
private void ThrowIfNotFound(string clientId)
{
if (!clients.ContainsKey(clientId))
{
throw new DomainObjectNotFoundException(clientId, "Contributors", typeof(AppDomainObject));
}
}
private void ThrowIfFound(string clientId, Func<string> message)
{
if (clients.ContainsKey(clientId))
{
var error = new ValidationError("Client id is alreay part of the app", "Id");
throw new ValidationException(message(), error);
}
}
private void ThrowIfSameName(string clientId, string name, Func<string> message)
{
if (string.Equals(clients[clientId].Name, name))
{
var error = new ValidationError("Client already has the name", "Id");
throw new ValidationException(message(), error);
}
}
}
}

11
src/Squidex.Write/Apps/AppCommandHandler.cs

@ -80,9 +80,9 @@ namespace Squidex.Write.Apps
{
return handler.UpdateAsync<AppDomainObject>(command, x =>
{
x.AttachClient(command, keyGenerator.GenerateKey());
x.AttachClient(command, keyGenerator.GenerateKey(), command.Timestamp.AddYears(1));
context.Succeed(x.Clients[command.ClientId]);
context.Succeed(x.Clients[command.Id]);
});
}
@ -100,12 +100,7 @@ namespace Squidex.Write.Apps
{
return handler.UpdateAsync<AppDomainObject>(command, x => x.RevokeClient(command));
}
protected Task On(ConfigureLanguages command, CommandContext context)
{
return handler.UpdateAsync<AppDomainObject>(command, x => x.ConfigureLanguages(command));
}
public Task<bool> HandleAsync(CommandContext context)
{
return context.IsHandled ? Task.FromResult(false) : this.DispatchActionAsync(context.Command, context);

77
src/Squidex.Write/Apps/AppContributors.cs

@ -0,0 +1,77 @@
// ==========================================================================
// AppContributors.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using Squidex.Core.Apps;
using Squidex.Infrastructure;
// ReSharper disable InvertIf
namespace Squidex.Write.Apps
{
public class AppContributors
{
private readonly Dictionary<string, PermissionLevel> contributors = new Dictionary<string, PermissionLevel>();
public void Assign(string contributorId, PermissionLevel permission)
{
Func<string> message = () => "Cannot assign contributor";
ThrowIfFound(contributorId, message);
ThrowIfNoOwner(c => c[contributorId] = permission, message);
contributors[contributorId] = permission;
}
public void Remove(string contributorId)
{
Func<string> message = () => "Cannot remove contributor";
ThrowIfNotFound(contributorId);
ThrowIfNoOwner(c => c.Remove(contributorId), message);
contributors.Remove(contributorId);
}
private void ThrowIfNotFound(string contributorId)
{
if (!contributors.ContainsKey(contributorId))
{
throw new DomainObjectNotFoundException(contributorId, "Contributors", typeof(AppDomainObject));
}
}
private void ThrowIfFound(string contributorId, Func<string> message)
{
PermissionLevel currentPermission;
if (contributors.TryGetValue(contributorId, out currentPermission))
{
var error = new ValidationError("Contributor is already part of the app with same permissions", "ContributorId");
throw new ValidationException(message(), error);
}
}
private void ThrowIfNoOwner(Action<Dictionary<string, PermissionLevel>> change, Func<string> message)
{
var contributorsCopy = new Dictionary<string, PermissionLevel>(contributors);
change(contributorsCopy);
if (contributorsCopy.All(x => x.Value != PermissionLevel.Owner))
{
var error = new ValidationError("Contributor is the last owner", "ContributorId");
throw new ValidationException(message(), error);
}
}
}
}

165
src/Squidex.Write/Apps/AppDomainObject.cs

@ -15,7 +15,6 @@ using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Dispatching;
using Squidex.Write.Apps.Commands;
using System.Collections.Generic;
using System.Linq;
using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.Reflection;
// ReSharper disable InvertIf
@ -24,9 +23,10 @@ namespace Squidex.Write.Apps
{
public sealed class AppDomainObject : DomainObject
{
private static readonly List<Language> DefaultLanguages = new List<Language> { Language.GetLanguage("en") };
private readonly Dictionary<string, AppClient> clients = new Dictionary<string, AppClient>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, PermissionLevel> contributors = new Dictionary<string, PermissionLevel>();
private static readonly Language DefaultLanguage = Language.GetLanguage("en");
private readonly AppContributors contributors = new AppContributors();
private readonly AppLanguages languages = new AppLanguages();
private readonly AppClients clients = new AppClients();
private string name;
public string Name
@ -34,14 +34,9 @@ namespace Squidex.Write.Apps
get { return name; }
}
public IReadOnlyDictionary<string, PermissionLevel> Contributors
{
get { return contributors; }
}
public IReadOnlyDictionary<string, AppClient> Clients
{
get { return clients; }
get { return clients.Clients; }
}
public AppDomainObject(Guid id, int version)
@ -56,7 +51,7 @@ namespace Squidex.Write.Apps
public void On(AppContributorAssigned @event)
{
contributors[@event.ContributorId] = @event.Permission;
contributors.Assign(@event.ContributorId, @event.Permission);
}
public void On(AppContributorRemoved @event)
@ -66,17 +61,32 @@ namespace Squidex.Write.Apps
public void On(AppClientAttached @event)
{
clients.Add(@event.ClientId, new AppClient(@event.ClientId, @event.ClientSecret, @event.ExpiresUtc));
clients.Add(@event.Id, @event.Secret, @event.ExpiresUtc);
}
public void On(AppClientRenamed @event)
{
clients.Rename(@event.Id, @event.Name);
}
public void On(AppClientRevoked @event)
{
clients.Remove(@event.ClientId);
clients.Revoke(@event.Id);
}
public void On(AppClientRenamed @event)
public void On(AppLanguageAdded @event)
{
clients[@event.ClientId].Rename(@event.Name);
languages.Add(@event.Language);
}
public void On(AppLanguageRemoved @event)
{
languages.Remove(@event.Language);
}
public void On(AppMasterLanguageSet @event)
{
languages.SetMasterLanguage(@event.Language);
}
protected override void DispatchEvent(Envelope<IEvent> @event)
@ -86,9 +96,7 @@ namespace Squidex.Write.Apps
public AppDomainObject Create(CreateApp command)
{
Func<string> message = () => "Cannot create app";
Guard.Valid(command, nameof(command), message);
Guard.Valid(command, nameof(command), () => "Cannot create app");
ThrowIfCreated();
@ -96,32 +104,49 @@ namespace Squidex.Write.Apps
RaiseEvent(CreateInitialOwner(command));
RaiseEvent(CreateInitialLanguage());
RaiseEvent(CreateInitialMasterLanguage());
return this;
}
public AppDomainObject AssignContributor(AssignContributor command)
{
Func<string> message = () => "Cannot assign contributor";
Guard.Valid(command, nameof(command), message);
Guard.Valid(command, nameof(command), () => "Cannot assign contributor");
ThrowIfNotCreated();
ThrowIfNoOwner(c => c[command.ContributorId] = command.Permission, message);
RaiseEvent(SimpleMapper.Map(command, new AppContributorAssigned()));
return this;
}
public AppDomainObject RenameClient(RenameClient command)
public AppDomainObject RemoveContributor(RemoveContributor command)
{
Guard.Valid(command, nameof(command), () => "Cannot remove contributor");
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppContributorRemoved()));
return this;
}
public AppDomainObject AttachClient(AttachClient command, string secret, DateTime expiresUtc)
{
Func<string> message = () => "Cannot rename client";
Guard.Valid(command, nameof(command), () => "Cannot attach client");
Guard.Valid(command, nameof(command), message);
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppClientAttached { Secret = secret, ExpiresUtc = expiresUtc }));
return this;
}
public AppDomainObject RenameClient(RenameClient command)
{
Guard.Valid(command, nameof(command), () => "Cannot rename client");
ThrowIfNotCreated();
ThrowIfClientNotFound(command.ClientId, message);
RaiseEvent(SimpleMapper.Map(command, new AppClientRenamed()));
@ -130,66 +155,56 @@ namespace Squidex.Write.Apps
public AppDomainObject RevokeClient(RevokeClient command)
{
Func<string> message = () => "Cannot revoke client";
Guard.Valid(command, nameof(command), message);
Guard.Valid(command, nameof(command), () => "Cannot revoke client");
ThrowIfNotCreated();
ThrowIfClientNotFound(command.ClientId, message);
RaiseEvent(SimpleMapper.Map(command, new AppClientRevoked()));
return this;
}
public AppDomainObject AttachClient(AttachClient command, string secret)
public AppDomainObject AddLanguage(AddLanguage command)
{
Func<string> message = () => "Cannot attach client";
Guard.Valid(command, nameof(command), () => "Cannot attach client");
Guard.Valid(command, nameof(command), () => "Cannot add language");
ThrowIfNotCreated();
ThrowIfClientFound(command.ClientId, message);
var expire = command.Timestamp.AddYears(1);
RaiseEvent(SimpleMapper.Map(command, new AppClientAttached { ClientSecret = secret, ExpiresUtc = expire }));
RaiseEvent(SimpleMapper.Map(command, new AppLanguageAdded()));
return this;
}
public AppDomainObject RemoveContributor(RemoveContributor command)
public AppDomainObject RemoveLanguage(RemoveLanguage command)
{
Func<string> message = () => "Cannot remove contributor";
Guard.Valid(command, nameof(command), () => "Cannot remove contributor");
Guard.Valid(command, nameof(command), () => "Cannot remove language");
ThrowIfNotCreated();
ThrowIfContributorNotFound(command.ContributorId, message);
ThrowIfNoOwner(c => c.Remove(command.ContributorId), message);
RaiseEvent(SimpleMapper.Map(command, new AppContributorRemoved()));
RaiseEvent(SimpleMapper.Map(command, new AppLanguageRemoved()));
return this;
}
public AppDomainObject ConfigureLanguages(ConfigureLanguages command)
public AppDomainObject SetMasterLanguage(SetMasterLanguage command)
{
Func<string> message = () => "Cannot configure languages";
Guard.Valid(command, nameof(command), message);
Guard.Valid(command, nameof(command), () => "Cannot set master language");
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppLanguagesConfigured()));
RaiseEvent(SimpleMapper.Map(command, new AppMasterLanguageSet()));
return this;
}
private static AppLanguagesConfigured CreateInitialLanguage()
private static AppLanguageAdded CreateInitialLanguage()
{
return new AppLanguagesConfigured { Languages = DefaultLanguages };
return new AppLanguageAdded { Language = DefaultLanguage };
}
private static AppMasterLanguageSet CreateInitialMasterLanguage()
{
return new AppMasterLanguageSet { Language = DefaultLanguage };
}
private static AppContributorAssigned CreateInitialOwner(IUserCommand command)
@ -212,49 +227,5 @@ namespace Squidex.Write.Apps
throw new DomainException("App has already been created.");
}
}
private void ThrowIfClientFound(string clientId, Func<string> message)
{
if (clients.ContainsKey(clientId))
{
var error = new ValidationError("Client id is alreay part of the app", "ClientName");
throw new ValidationException(message(), error);
}
}
private void ThrowIfClientNotFound(string clientId, Func<string> message)
{
if (!clients.ContainsKey(clientId))
{
var error = new ValidationError("Client is not part of the app", "ClientName");
throw new ValidationException(message(), error);
}
}
private void ThrowIfContributorNotFound(string contributorId, Func<string> message)
{
if (!contributors.ContainsKey(contributorId))
{
var error = new ValidationError("Contributor is not part of the app", "ContributorId");
throw new ValidationException(message(), error);
}
}
private void ThrowIfNoOwner(Action<Dictionary<string, PermissionLevel>> change, Func<string> message)
{
var contributorsCopy = new Dictionary<string, PermissionLevel>(contributors);
change(contributorsCopy);
if (contributorsCopy.All(x => x.Value != PermissionLevel.Owner))
{
var error = new ValidationError("Contributor is the last owner", "ContributorId");
throw new ValidationException(message(), error);
}
}
}
}

83
src/Squidex.Write/Apps/AppLanguages.cs

@ -0,0 +1,83 @@
// ==========================================================================
// AppLanguages.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Infrastructure;
// ReSharper disable InvertIf
namespace Squidex.Write.Apps
{
public class AppLanguages
{
private readonly HashSet<Language> languages = new HashSet<Language>();
private Language masterLanguage;
public IReadOnlyCollection<Language> Languages
{
get { return languages; }
}
public void Add(Language language)
{
Func<string> message = () => "Cannot add language";
ThrowIfFound(language, message);
languages.Add(language);
}
public void Remove(Language language)
{
Func<string> message = () => "Cannot remove language";
ThrowIfNotFound(language);
ThrowIfMasterLanguage(language, message);
languages.Remove(language);
}
public void SetMasterLanguage(Language language)
{
Func<string> message = () => "Cannot set master language";
ThrowIfNotFound(language);
ThrowIfMasterLanguage(language, message);
masterLanguage = language;
}
private void ThrowIfNotFound(Language language)
{
if (!languages.Contains(language))
{
throw new DomainObjectNotFoundException(language.Iso2Code, "Languages", typeof(AppDomainObject));
}
}
private void ThrowIfFound(Language language, Func<string> message)
{
if (languages.Contains(language))
{
var error = new ValidationError("Language id is alreay part of the app", "Language");
throw new ValidationException(message(), error);
}
}
private void ThrowIfMasterLanguage(Language language, Func<string> message)
{
if (masterLanguage != null && masterLanguage.Equals(language))
{
var error = new ValidationError("Language is the master language", "Language");
throw new ValidationException(message(), error);
}
}
}
}

26
src/Squidex.Write/Apps/Commands/AddLanguage.cs

@ -0,0 +1,26 @@
// ==========================================================================
// AddLanguage.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Infrastructure;
namespace Squidex.Write.Apps.Commands
{
public sealed class AddLanguage : AppAggregateCommand, IValidatable
{
public Language Language { get; set; }
public void Validate(IList<ValidationError> errors)
{
if (Language == null)
{
errors.Add(new ValidationError("Language cannot be null", nameof(Language)));
}
}
}
}

6
src/Squidex.Write/Apps/Commands/AttachClient.cs

@ -15,15 +15,15 @@ namespace Squidex.Write.Apps.Commands
{
public sealed class AttachClient : AppAggregateCommand, ITimestampCommand, IValidatable
{
public string ClientId { get; set; }
public string Id { get; set; }
public DateTime Timestamp { get; set; }
public void Validate(IList<ValidationError> errors)
{
if (!ClientId.IsSlug())
if (!Id.IsSlug())
{
errors.Add(new ValidationError("Client id must be a valid slug", nameof(ClientId)));
errors.Add(new ValidationError("Client id must be a valid slug", nameof(Id)));
}
}
}

26
src/Squidex.Write/Apps/Commands/RemoveLanguage.cs

@ -0,0 +1,26 @@
// ==========================================================================
// RemoveLanguage.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Infrastructure;
namespace Squidex.Write.Apps.Commands
{
public sealed class RemoveLanguage : AppAggregateCommand, IValidatable
{
public Language Language { get; set; }
public void Validate(IList<ValidationError> errors)
{
if (Language == null)
{
errors.Add(new ValidationError("Language cannot be null", nameof(Language)));
}
}
}
}

6
src/Squidex.Write/Apps/Commands/RenameClient.cs

@ -13,7 +13,7 @@ namespace Squidex.Write.Apps.Commands
{
public class RenameClient : AppAggregateCommand, IValidatable
{
public string ClientId { get; set; }
public string Id { get; set; }
public string Name { get; set; }
@ -24,9 +24,9 @@ namespace Squidex.Write.Apps.Commands
errors.Add(new ValidationError("Name cannot be null or empty", nameof(Name)));
}
if (!ClientId.IsSlug())
if (!Id.IsSlug())
{
errors.Add(new ValidationError("Client id must be a valid slug", nameof(ClientId)));
errors.Add(new ValidationError("Client id must be a valid slug", nameof(Id)));
}
}
}

6
src/Squidex.Write/Apps/Commands/RevokeClient.cs

@ -13,13 +13,13 @@ namespace Squidex.Write.Apps.Commands
{
public class RevokeClient : AppAggregateCommand, IValidatable
{
public string ClientId { get; set; }
public string Id { get; set; }
public void Validate(IList<ValidationError> errors)
{
if (!ClientId.IsSlug())
if (!Id.IsSlug())
{
errors.Add(new ValidationError("Client id must be a valid slug", nameof(ClientId)));
errors.Add(new ValidationError("Client id must be a valid slug", nameof(Id)));
}
}
}

26
src/Squidex.Write/Apps/Commands/SetMasterLanguage.cs

@ -0,0 +1,26 @@
// ==========================================================================
// SetMasterLanguage.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Infrastructure;
namespace Squidex.Write.Apps.Commands
{
public sealed class SetMasterLanguage : AppAggregateCommand, IValidatable
{
public Language Language { get; set; }
public void Validate(IList<ValidationError> errors)
{
if (Language == null)
{
errors.Add(new ValidationError("Language cannot be null", nameof(Language)));
}
}
}
}

4
src/Squidex/Config/Identity/LazyClientStore.cs

@ -52,7 +52,7 @@ namespace Squidex.Config.Identity
var app = await appProvider.FindAppByNameAsync(token[0]);
var appClient = app?.Clients.FirstOrDefault(x => x.ClientId == token[1]);
var appClient = app?.Clients.FirstOrDefault(x => x.Id == token[1]);
if (appClient == null)
{
@ -71,7 +71,7 @@ namespace Squidex.Config.Identity
{
ClientId = id,
ClientName = id,
ClientSecrets = new List<Secret> { new Secret(appClient.ClientSecret.Sha512(), appClient.ExpiresUtc) },
ClientSecrets = new List<Secret> { new Secret(appClient.Secret.Sha512(), appClient.ExpiresUtc) },
AccessTokenLifetime = (int)TimeSpan.FromDays(30).TotalSeconds,
AllowedGrantTypes = GrantTypes.ClientCredentials,
AllowedScopes = new List<string>

10
src/Squidex/Controllers/Api/Apps/AppClientsController.cs

@ -96,7 +96,7 @@ namespace Squidex.Controllers.Api.Apps
/// Updates an app client.
/// </summary>
/// <param name="app">The name of the app.</param>
/// <param name="client">The id of the client that must be updated.</param>
/// <param name="clientId">The id of the client that must be updated.</param>
/// <param name="request">Client object that needs to be added to the app.</param>
/// <returns>
/// 201 => Client key generated.
@ -105,7 +105,7 @@ namespace Squidex.Controllers.Api.Apps
[HttpPut]
[Route("apps/{app}/clients/{client}/")]
[ProducesResponseType(typeof(ClientDto[]), 201)]
public async Task<IActionResult> PutClient(string app, string client, [FromBody] RenameClientDto request)
public async Task<IActionResult> PutClient(string app, string clientId, [FromBody] RenameClientDto request)
{
await CommandBus.PublishAsync(SimpleMapper.Map(request, new RenameClient()));
@ -116,16 +116,16 @@ namespace Squidex.Controllers.Api.Apps
/// Revoke an app client
/// </summary>
/// <param name="app">The name of the app.</param>
/// <param name="client">The id of the client that must be deleted.</param>
/// <param name="clientId">The id of the client that must be deleted.</param>
/// <returns>
/// 404 => App not found or client not found.
/// 204 => Client revoked.
/// </returns>
[HttpDelete]
[Route("apps/{app}/clients/{client}/")]
public async Task<IActionResult> DeleteClient(string app, string client)
public async Task<IActionResult> DeleteClient(string app, string clientId)
{
await CommandBus.PublishAsync(new RevokeClient { ClientId = client });
await CommandBus.PublishAsync(new RevokeClient { Id = clientId });
return NoContent();
}

2
src/Squidex/Controllers/Api/Apps/Models/AttachClientDto.cs

@ -17,6 +17,6 @@ namespace Squidex.Controllers.Api.Apps.Models
/// </summary>
[Required]
[RegularExpression("^[a-z0-9]+(\\-[a-z0-9]+)*$")]
public string ClientId { get; set; }
public string Id { get; set; }
}
}

12
src/Squidex/Controllers/Api/Apps/Models/ClientDto.cs

@ -17,24 +17,24 @@ namespace Squidex.Controllers.Api.Apps.Models
/// The client id.
/// </summary>
[Required]
public string ClientId { get; set; }
public string Id { get; set; }
/// <summary>
/// The client secret.
/// </summary>
[Required]
public string ClientSecret { get; set; }
public string Secret { get; set; }
/// <summary>
/// The date and time when the client key expires.
/// The client name.
/// </summary>
[Required]
public DateTime ExpiresUtc { get; set; }
public string Name { get; set; }
/// <summary>
/// The client name.
/// The date and time when the client key expires.
/// </summary>
[Required]
public string Name { get; set; }
public DateTime ExpiresUtc { get; set; }
}
}

2
src/Squidex/Pipeline/AppFilterAttribute.cs

@ -77,7 +77,7 @@ namespace Squidex.Pipeline
clientId = clientId.Split(':')[0];
var contributor = app.Clients.FirstOrDefault(x => string.Equals(x.ClientId, clientId, StringComparison.OrdinalIgnoreCase));
var contributor = app.Clients.FirstOrDefault(x => string.Equals(x.Id, clientId, StringComparison.OrdinalIgnoreCase));
return contributor != null ? PermissionLevel.Owner : PermissionLevel.Editor;
}

2
src/Squidex/app/components/internal/app/settings/clients-page.component.html

@ -37,7 +37,7 @@
<tr>
<td>Client Secret:</td>
<td>
<input readonly class="form-control" [attr.value]="client.clientSecret" />
<input readonly class="form-control" [attr.value]="client.secret" />
</td>
</tr>
<tr>

4
src/Squidex/app/components/internal/app/settings/clients-page.component.ts

@ -82,11 +82,11 @@ export class ClientsPageComponent implements Ng2.OnInit {
}
public fullAppName(client: AppClientDto): string {
return this.appName + ':' + client.clientName;
return this.appName + ':' + client.id;
}
public revokeClient(client: AppClientDto) {
this.appClientsService.deleteClient(this.appName, client.clientName)
this.appClientsService.deleteClient(this.appName, client.id)
.subscribe(() => {
this.appClients.splice(this.appClients.indexOf(client), 1);
}, error => {

21
src/Squidex/app/shared/services/app-clients.service.spec.ts

@ -34,12 +34,14 @@ describe('AppClientsService', () => {
new Ng2Http.Response(
new Ng2Http.ResponseOptions({
body: [{
clientName: 'client1',
clientSecret: 'secret1',
id: 'client1',
name: 'Client1',
secret: 'secret1',
expiresUtc: '2016-12-12T10:10'
}, {
clientName: 'client2',
clientSecret: 'secret2',
id: 'client2',
name: 'Client2',
secret: 'secret2',
expiresUtc: '2016-11-11T10:10'
}]
})
@ -55,8 +57,8 @@ describe('AppClientsService', () => {
expect(clients).toEqual(
[
new AppClientDto('client1', 'secret1', DateTime.parseISO_UTC('2016-12-12T10:10')),
new AppClientDto('client2', 'secret2', DateTime.parseISO_UTC('2016-11-11T10:10')),
new AppClientDto('client1', 'Client1', 'secret1', DateTime.parseISO_UTC('2016-12-12T10:10')),
new AppClientDto('client2', 'Client2', 'secret2', DateTime.parseISO_UTC('2016-11-11T10:10')),
]);
authService.verifyAll();
@ -70,8 +72,9 @@ describe('AppClientsService', () => {
new Ng2Http.Response(
new Ng2Http.ResponseOptions({
body: {
clientName: 'client1',
clientSecret: 'secret1',
id: 'client1',
name: 'Client1',
secret: 'secret1',
expiresUtc: '2016-12-12T10:10'
}
})
@ -86,7 +89,7 @@ describe('AppClientsService', () => {
});
expect(client).toEqual(
new AppClientDto('client1', 'secret1', DateTime.parseISO_UTC('2016-12-12T10:10')));
new AppClientDto('client1', 'Client1', 'secret1', DateTime.parseISO_UTC('2016-12-12T10:10')));
authService.verifyAll();
});

13
src/Squidex/app/shared/services/app-clients.service.ts

@ -15,8 +15,9 @@ import { AuthService } from './auth.service';
export class AppClientDto {
constructor(
public readonly clientName: string,
public readonly clientSecret: string,
public readonly id: string,
public readonly name: string,
public readonly secret: string,
public readonly expiresUtc: DateTime
) {
}
@ -24,7 +25,7 @@ export class AppClientDto {
export class AppClientCreateDto {
constructor(
public readonly clientName: string
public readonly id: string
) {
}
}
@ -53,7 +54,7 @@ export class AppClientsService {
const items: any[] = response;
return items.map(item => {
return new AppClientDto(item.clientName, item.clientSecret, DateTime.parseISO_UTC(item.expiresUtc));
return new AppClientDto(item.id, item.name, item.secret, DateTime.parseISO_UTC(item.expiresUtc));
});
});
}
@ -61,7 +62,7 @@ export class AppClientsService {
public postClient(appName: string, client: AppClientCreateDto): Observable<AppClientDto> {
return this.authService.authPost(this.apiUrl.buildUrl(`api/apps/${appName}/clients`), client)
.map(response => response.json())
.map(response => new AppClientDto(response.clientName, response.clientSecret, DateTime.parseISO_UTC(response.expiresUtc)))
.map(response => new AppClientDto(response.id, response.name, response.secret, DateTime.parseISO_UTC(response.expiresUtc)))
.catch(response => {
if (response.status === 400) {
return Observable.throw('A client with the same name already exists.');
@ -82,7 +83,7 @@ export class AppClientsService {
})
});
const body = `grant_type=client_credentials&scope=squidex-api&client_id=${appName}:${client.clientName}&client_secret=${client.clientSecret}`;
const body = `grant_type=client_credentials&scope=squidex-api&client_id=${appName}:${client.id}&client_secret=${client.secret}`;
return this.http.post(this.apiUrl.buildUrl('identity-server/connect/token'), body, options)
.map(response => response.json())

25
tests/Squidex.Write.Tests/Apps/AppCommandHandlerTests.cs

@ -34,6 +34,7 @@ namespace Squidex.Write.Apps
private readonly AppCommandHandler sut;
private readonly AppDomainObject app;
private readonly UserToken subjectId = new UserToken("subject", Guid.NewGuid().ToString());
private readonly DateTime expiresUtc = DateTime.UtcNow.AddYears(1);
private readonly string contributorId = Guid.NewGuid().ToString();
private readonly string clientSecret = Guid.NewGuid().ToString();
private readonly string clientName = "client";
@ -77,20 +78,6 @@ namespace Squidex.Write.Apps
Assert.Equal(command.AggregateId, context.Result<Guid>());
}
[Fact]
public async Task ConfigureLanguages_should_update_domain_object()
{
CreateApp();
var command = new ConfigureLanguages { AggregateId = Id, Languages = new List<Language> { Language.GetLanguage("de") } };
var context = new CommandContext(command);
await TestUpdate(app, async _ =>
{
await sut.HandleAsync(context);
});
}
[Fact]
public async Task AssignContributor_should_throw_if_user_not_found()
@ -164,7 +151,7 @@ namespace Squidex.Write.Apps
var timestamp = DateTime.Today;
var command = new AttachClient { ClientId = clientName, AggregateId = Id, Timestamp = timestamp };
var command = new AttachClient { Id = clientName, AggregateId = Id, Timestamp = timestamp };
var context = new CommandContext(command);
await TestUpdate(app, async _ =>
@ -182,9 +169,9 @@ namespace Squidex.Write.Apps
public async Task RenameClient_should_update_domain_object()
{
CreateApp()
.AttachClient(new AttachClient { ClientId = clientName }, clientSecret);
.AttachClient(new AttachClient { Id = clientName }, clientSecret, expiresUtc);
var command = new RenameClient { AggregateId = Id, ClientId = clientName, Name = "New Name" };
var command = new RenameClient { AggregateId = Id, Id = clientName, Name = "New Name" };
var context = new CommandContext(command);
await TestUpdate(app, async _ =>
@ -197,9 +184,9 @@ namespace Squidex.Write.Apps
public async Task RevokeClient_should_update_domain_object()
{
CreateApp()
.AttachClient(new AttachClient { ClientId = clientName }, clientSecret);
.AttachClient(new AttachClient { Id = clientName }, clientSecret, expiresUtc);
var command = new RevokeClient { AggregateId = Id, ClientId = clientName };
var command = new RevokeClient { AggregateId = Id, Id = clientName };
var context = new CommandContext(command);
await TestUpdate(app, async _ =>

91
tests/Squidex.Write.Tests/Apps/AppDomainObjectTests.cs

@ -7,7 +7,6 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Squidex.Core.Apps;
@ -27,11 +26,11 @@ namespace Squidex.Write.Apps
private const string TestName = "app";
private readonly AppDomainObject sut;
private readonly UserToken user = new UserToken("subject", Guid.NewGuid().ToString());
private readonly DateTime expiresUtc = DateTime.UtcNow.AddYears(1);
private readonly string contributorId = Guid.NewGuid().ToString();
private readonly string clientSecret = Guid.NewGuid().ToString();
private readonly string clientId = "client";
private readonly string clientNewName = "My Client";
private readonly List<Language> languages = new List<Language> { Language.GetLanguage("de") };
public AppDomainObjectTests()
{
@ -58,7 +57,6 @@ namespace Squidex.Write.Apps
sut.Create(new CreateApp { Name = TestName, User = user });
Assert.Equal(TestName, sut.Name);
Assert.Equal(PermissionLevel.Owner, sut.Contributors[user.Identifier]);
sut.GetUncomittedEvents().Select(x => x.Payload).ToArray()
.ShouldBeEquivalentTo(
@ -66,7 +64,8 @@ namespace Squidex.Write.Apps
{
new AppCreated { Name = TestName },
new AppContributorAssigned { ContributorId = user.Identifier, Permission = PermissionLevel.Owner },
new AppLanguagesConfigured { Languages= new List<Language> { Language.GetLanguage("en") } }
new AppLanguageAdded { Language = Language.GetLanguage("en") },
new AppMasterLanguageSet { Language = Language.GetLanguage("en") }
});
}
@ -97,8 +96,6 @@ namespace Squidex.Write.Apps
sut.AssignContributor(new AssignContributor { ContributorId = contributorId, Permission = PermissionLevel.Editor });
Assert.Equal(PermissionLevel.Editor, sut.Contributors[contributorId]);
sut.GetUncomittedEvents().Select(x => x.Payload).ToArray()
.ShouldBeEquivalentTo(
new IEvent[]
@ -132,9 +129,7 @@ namespace Squidex.Write.Apps
{
CreateApp();
sut.AssignContributor(new AssignContributor { ContributorId = contributorId, Permission = PermissionLevel.Editor });
Assert.Throws<ValidationException>(() => sut.RemoveContributor(new RemoveContributor { ContributorId = "not-found" }));
Assert.Throws<DomainObjectNotFoundException>(() => sut.RemoveContributor(new RemoveContributor { ContributorId = "not-found" }));
}
[Fact]
@ -144,9 +139,7 @@ namespace Squidex.Write.Apps
sut.AssignContributor(new AssignContributor { ContributorId = contributorId, Permission = PermissionLevel.Editor });
sut.RemoveContributor(new RemoveContributor { ContributorId = contributorId });
Assert.False(sut.Contributors.ContainsKey(contributorId));
sut.GetUncomittedEvents().Select(x => x.Payload).Skip(1).ToArray()
.ShouldBeEquivalentTo(
new IEvent[]
@ -155,42 +148,10 @@ namespace Squidex.Write.Apps
});
}
[Fact]
public void ConfigureLanguages_should_throw_if_not_created()
{
Assert.Throws<DomainException>(() => sut.ConfigureLanguages(new ConfigureLanguages { Languages = languages }));
}
[Fact]
public void ConfigureLanguages_should_throw_if_command_is_not_valid()
{
CreateApp();
Assert.Throws<ValidationException>(() => sut.ConfigureLanguages(new ConfigureLanguages()));
Assert.Throws<ValidationException>(() => sut.ConfigureLanguages(new ConfigureLanguages { Languages = new List<Language>() }));
}
[Fact]
public void ConfigureLanguages_should_create_events()
{
CreateApp();
sut.ConfigureLanguages(new ConfigureLanguages { Languages = languages });
Assert.False(sut.Contributors.ContainsKey(contributorId));
sut.GetUncomittedEvents().Select(x => x.Payload).ToArray()
.ShouldBeEquivalentTo(
new IEvent[]
{
new AppLanguagesConfigured { Languages = languages }
});
}
[Fact]
public void AttachClient_should_throw_if_not_created()
{
Assert.Throws<DomainException>(() => sut.AttachClient(new AttachClient { ClientId = clientId }, clientSecret));
Assert.Throws<DomainException>(() => sut.AttachClient(new AttachClient { Id = clientId }, clientSecret, expiresUtc));
}
[Fact]
@ -198,8 +159,8 @@ namespace Squidex.Write.Apps
{
CreateApp();
Assert.Throws<ValidationException>(() => sut.AttachClient(new AttachClient(), clientSecret));
Assert.Throws<ValidationException>(() => sut.AttachClient(new AttachClient { ClientId = string.Empty }, clientSecret));
Assert.Throws<ValidationException>(() => sut.AttachClient(new AttachClient(), clientSecret, expiresUtc));
Assert.Throws<ValidationException>(() => sut.AttachClient(new AttachClient { Id = string.Empty }, clientSecret, expiresUtc));
}
[Fact]
@ -207,9 +168,9 @@ namespace Squidex.Write.Apps
{
CreateApp();
sut.AttachClient(new AttachClient { ClientId = clientId }, clientSecret);
sut.AttachClient(new AttachClient { Id = clientId }, clientSecret, expiresUtc);
Assert.Throws<ValidationException>(() => sut.AttachClient(new AttachClient { ClientId = clientId }, clientSecret));
Assert.Throws<ValidationException>(() => sut.AttachClient(new AttachClient { Id = clientId }, clientSecret, expiresUtc));
}
[Fact]
@ -219,22 +180,20 @@ namespace Squidex.Write.Apps
CreateApp();
sut.AttachClient(new AttachClient { ClientId = clientId, Timestamp = now }, clientSecret);
Assert.False(sut.Contributors.ContainsKey(contributorId));
sut.AttachClient(new AttachClient { Id = clientId, Timestamp = now }, clientSecret, expiresUtc);
sut.GetUncomittedEvents().Select(x => x.Payload).ToArray()
.ShouldBeEquivalentTo(
new IEvent[]
{
new AppClientAttached { ClientId = clientId, ClientSecret = clientSecret, ExpiresUtc = now.AddYears(1) }
new AppClientAttached { Id = clientId, Secret = clientSecret, ExpiresUtc = expiresUtc }
});
}
[Fact]
public void RevokeKey_should_throw_if_not_created()
{
Assert.Throws<DomainException>(() => sut.RevokeClient(new RevokeClient { ClientId = "not-found" }));
Assert.Throws<DomainException>(() => sut.RevokeClient(new RevokeClient { Id = "not-found" }));
}
[Fact]
@ -243,7 +202,7 @@ namespace Squidex.Write.Apps
CreateApp();
Assert.Throws<ValidationException>(() => sut.RevokeClient(new RevokeClient()));
Assert.Throws<ValidationException>(() => sut.RevokeClient(new RevokeClient { ClientId = string.Empty }));
Assert.Throws<ValidationException>(() => sut.RevokeClient(new RevokeClient { Id = string.Empty }));
}
[Fact]
@ -251,7 +210,7 @@ namespace Squidex.Write.Apps
{
CreateApp();
Assert.Throws<ValidationException>(() => sut.RevokeClient(new RevokeClient { ClientId = "not-found" }));
Assert.Throws<DomainObjectNotFoundException>(() => sut.RevokeClient(new RevokeClient { Id = "not-found" }));
}
[Fact]
@ -259,21 +218,21 @@ namespace Squidex.Write.Apps
{
CreateApp();
sut.AttachClient(new AttachClient { ClientId = clientId }, clientSecret);
sut.RevokeClient(new RevokeClient { ClientId = clientId });
sut.AttachClient(new AttachClient { Id = clientId }, clientSecret, expiresUtc);
sut.RevokeClient(new RevokeClient { Id = clientId });
sut.GetUncomittedEvents().Select(x => x.Payload).Skip(1).ToArray()
.ShouldBeEquivalentTo(
new IEvent[]
{
new AppClientRevoked { ClientId = clientSecret }
new AppClientRevoked { Id = clientSecret }
});
}
[Fact]
public void RenameKey_should_throw_if_not_created()
{
Assert.Throws<DomainException>(() => sut.RenameClient(new RenameClient { ClientId = "not-found", Name = clientNewName }));
Assert.Throws<DomainException>(() => sut.RenameClient(new RenameClient { Id = "not-found", Name = clientNewName }));
}
[Fact]
@ -282,7 +241,7 @@ namespace Squidex.Write.Apps
CreateApp();
Assert.Throws<ValidationException>(() => sut.RenameClient(new RenameClient()));
Assert.Throws<ValidationException>(() => sut.RenameClient(new RenameClient { ClientId = string.Empty }));
Assert.Throws<ValidationException>(() => sut.RenameClient(new RenameClient { Id = string.Empty }));
}
[Fact]
@ -290,7 +249,7 @@ namespace Squidex.Write.Apps
{
CreateApp();
Assert.Throws<ValidationException>(() => sut.RenameClient(new RenameClient { ClientId = "not-found", Name = clientNewName }));
Assert.Throws<DomainObjectNotFoundException>(() => sut.RenameClient(new RenameClient { Id = "not-found", Name = clientNewName }));
}
[Fact]
@ -298,16 +257,14 @@ namespace Squidex.Write.Apps
{
CreateApp();
sut.AttachClient(new AttachClient { ClientId = clientId }, clientSecret);
sut.RenameClient(new RenameClient { ClientId = clientId, Name = clientNewName });
Assert.Equal(clientNewName, sut.Clients[clientId].Name);
sut.AttachClient(new AttachClient { Id = clientId }, clientSecret, expiresUtc);
sut.RenameClient(new RenameClient { Id = clientId, Name = clientNewName });
sut.GetUncomittedEvents().Select(x => x.Payload).Skip(1).ToArray()
.ShouldBeEquivalentTo(
new IEvent[]
{
new AppClientRenamed { ClientId = clientId, Name = clientNewName }
new AppClientRenamed { Id = clientId, Name = clientNewName }
});
}

Loading…
Cancel
Save