Browse Source

User in graphql. (#682)

* User in graphql.

* Formatting.
pull/687/head
Sebastian Stehle 5 years ago
committed by GitHub
parent
commit
b3e136943c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 32
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs
  2. 16
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetGraphType.cs
  3. 16
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentFields.cs
  4. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentGraphType.cs
  5. 9
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/EntityResolvers.cs
  6. 58
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/UserGraphType.cs
  7. 2
      backend/src/Squidex.Shared/Users/ClientUser.cs
  8. 5
      backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDto.cs
  9. 13
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterCompareTests.cs
  10. 11
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs
  11. 46
      backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/UserMocks.cs
  12. 6
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppDomainObjectTests.cs
  13. 6
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/Guards/GuardAppContributorsTests.cs
  14. 5
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/InvitationEventConsumerTests.cs
  15. 22
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/InviteUserCommandMiddlewareTests.cs
  16. 6
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Plans/UsageNotifierGrainTests.cs
  17. 30
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/UserMappingTests.cs
  18. 25
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/CommentTriggerHandlerTests.cs
  19. 6
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/DomainObject/CommentsCommandMiddlewareTests.cs
  20. 2
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs
  21. 14
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs
  22. 24
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestAsset.cs
  23. 28
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestContent.cs
  24. 68
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Notifications/NotificationEmailSenderTests.cs
  25. 30
      backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/Mocks.cs

32
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs

@ -16,6 +16,7 @@ using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Json.Objects;
using Squidex.Log;
using Squidex.Shared.Users;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
@ -24,6 +25,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
private static readonly List<IEnrichedAssetEntity> EmptyAssets = new List<IEnrichedAssetEntity>();
private static readonly List<IEnrichedContentEntity> EmptyContents = new List<IEnrichedContentEntity>();
private readonly IDataLoaderContextAccessor dataLoaders;
private readonly IUserResolver userResolver;
public IUrlGenerator UrlGenerator { get; }
@ -35,10 +37,13 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
public GraphQLExecutionContext(IAssetQueryService assetQuery, IContentQueryService contentQuery,
Context context,
IDataLoaderContextAccessor dataLoaders, ICommandBus commandBus, IUrlGenerator urlGenerator, ISemanticLog log)
IDataLoaderContextAccessor dataLoaders,
ICommandBus commandBus, IUrlGenerator urlGenerator, IUserResolver userResolver,
ISemanticLog log)
: base(assetQuery, contentQuery)
{
this.dataLoaders = dataLoaders;
this.userResolver = userResolver;
CommandBus = commandBus;
@ -51,6 +56,20 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
Log = log;
}
public async Task<IUser> FindUserAsync(RefToken refToken)
{
if (refToken.IsClient)
{
return new ClientUser(refToken);
}
else
{
var dataLoader = GetUserLoader();
return await dataLoader.LoadAsync(refToken.Identifier).GetResultAsync();
}
}
public async Task<IEnrichedAssetEntity?> FindAssetAsync(DomainId id)
{
var dataLoader = GetAssetsLoader();
@ -115,6 +134,17 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
});
}
private IDataLoader<string, IUser> GetUserLoader()
{
return dataLoaders.Context.GetOrAddBatchLoader<string, IUser>(nameof(GetUserLoader),
async batch =>
{
var result = await userResolver.QueryManyAsync(batch.ToArray());
return result;
});
}
private static async Task<IReadOnlyList<T>> LoadManyAsync<TKey, T>(IDataLoader<TKey, T> dataLoader, ICollection<TKey> keys) where T : class
{
var contents = await Task.WhenAll(keys.Select(x => dataLoader.LoadAsync(x).GetResultAsync()));

16
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetGraphType.cs

@ -54,6 +54,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Assets
Description = "The user that has created the asset."
});
AddField(new FieldType
{
Name = "createdByUser",
ResolvedType = UserGraphType.NonNull,
Resolver = EntityResolvers.CreatedByUser,
Description = "The full info of the user that has created the asset."
});
AddField(new FieldType
{
Name = "lastModified",
@ -70,6 +78,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Assets
Description = "The user that has updated the asset last."
});
AddField(new FieldType
{
Name = "lastModifiedByUser",
ResolvedType = UserGraphType.NonNull,
Resolver = EntityResolvers.LastModifiedByUser,
Description = "The full info of the user that has created the asset."
});
AddField(new FieldType
{
Name = "mimeType",

16
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentFields.cs

@ -46,6 +46,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents
Description = "The user that has created the content."
};
public static readonly FieldType CreatedByUser = new FieldType
{
Name = "createdByUser",
ResolvedType = UserGraphType.NonNull,
Resolver = EntityResolvers.CreatedByUser,
Description = "The full info of the user that has created the content."
};
public static readonly FieldType LastModified = new FieldType
{
Name = "lastModified",
@ -62,6 +70,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents
Description = "The user that has updated the content last."
};
public static readonly FieldType LastModifiedByUser = new FieldType
{
Name = "lastModifiedByUser",
ResolvedType = UserGraphType.NonNull,
Resolver = EntityResolvers.LastModifiedByUser,
Description = "The full info of the user that has updated the content last."
};
public static readonly FieldType Status = new FieldType
{
Name = "status",

2
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentGraphType.cs

@ -27,8 +27,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents
AddField(ContentFields.Version);
AddField(ContentFields.Created);
AddField(ContentFields.CreatedBy);
AddField(ContentFields.CreatedByUser);
AddField(ContentFields.LastModified);
AddField(ContentFields.LastModifiedBy);
AddField(ContentFields.LastModifiedByUser);
AddField(ContentFields.Status);
AddField(ContentFields.StatusColor);

9
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/EntityResolvers.cs

@ -7,6 +7,8 @@
using System;
using GraphQL.Resolvers;
using Squidex.Infrastructure;
using Squidex.Shared.Users;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives
{
@ -15,13 +17,20 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives
public static readonly IFieldResolver Id = Resolve<IEntity>(x => x.Id.ToString());
public static readonly IFieldResolver Created = Resolve<IEntity>(x => x.Created.ToDateTimeUtc());
public static readonly IFieldResolver CreatedBy = Resolve<IEntityWithCreatedBy>(x => x.CreatedBy.ToString());
public static readonly IFieldResolver CreatedByUser = ResolveUser<IEntityWithCreatedBy>(x => x.CreatedBy);
public static readonly IFieldResolver LastModified = Resolve<IEntity>(x => x.LastModified.ToDateTimeUtc());
public static readonly IFieldResolver LastModifiedBy = Resolve<IEntityWithLastModifiedBy>(x => x.LastModifiedBy.ToString());
public static readonly IFieldResolver LastModifiedByUser = ResolveUser<IEntityWithLastModifiedBy>(x => x.LastModifiedBy);
public static readonly IFieldResolver Version = Resolve<IEntityWithVersion>(x => x.Version);
private static IFieldResolver Resolve<TSource>(Func<TSource, object> resolver)
{
return Resolvers.Sync(resolver);
}
private static IFieldResolver ResolveUser<TSource>(Func<TSource, RefToken> resolver)
{
return Resolvers.Async<TSource, IUser>((source, _, context) => context.FindUserAsync(resolver(source)));
}
}
}

58
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/UserGraphType.cs

@ -0,0 +1,58 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using GraphQL.Resolvers;
using GraphQL.Types;
using Squidex.Shared.Identity;
using Squidex.Shared.Users;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
internal sealed class UserGraphType : ObjectGraphType<IUser>
{
public static readonly IGraphType Nullable = new UserGraphType();
public static readonly IGraphType NonNull = new NonNullGraphType(Nullable);
public UserGraphType()
{
Name = "User";
AddField(new FieldType
{
Name = "id",
Resolver = Resolve(x => x.Id),
ResolvedType = AllTypes.NonNullString,
Description = "The id of the user."
});
AddField(new FieldType
{
Name = "displayName",
Resolver = Resolve(x => x.Claims.DisplayName()),
ResolvedType = AllTypes.String,
Description = "The display name of the user."
});
AddField(new FieldType
{
Name = "email",
Resolver = Resolve(x => x.Email),
ResolvedType = AllTypes.String,
Description = "The email of the user."
});
Description = "A user that created or modified a content or asset.";
}
private static IFieldResolver Resolve<T>(Func<IUser, T> resolver)
{
return Resolvers.Sync(resolver);
}
}
}

2
backend/src/Squidex.Shared/Users/ClientUser.cs

@ -49,7 +49,7 @@ namespace Squidex.Shared.Users
claims = new List<Claim>
{
new Claim(OpenIdClaims.ClientId, token.Identifier),
new Claim(SquidexClaimTypes.DisplayName, token.ToString())
new Claim(SquidexClaimTypes.DisplayName, token.Identifier)
};
}
}

5
backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDto.cs

@ -106,7 +106,6 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
if (resources.CanCreateContent(Name))
{
AddPostLink("contents/create", resources.Url<ContentsController>(x => nameof(x.PostContent), values));
AddPostLink("contents/create/publish", resources.Url<ContentsController>(x => nameof(x.PostContent), values) + "?publish=true");
}
@ -130,10 +129,10 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
AddPutLink("fields/order", resources.Url<SchemaFieldsController>(x => nameof(x.PutSchemaFieldOrdering), values));
AddPutLink("update", resources.Url<SchemasController>(x => nameof(x.PutSchema), values));
AddPutLink("update/category", resources.Url<SchemasController>(x => nameof(x.PutCategory), values));
AddPutLink("update/rules", resources.Url<SchemasController>(x => nameof(x.PutRules), values));
AddPutLink("update/sync", resources.Url<SchemasController>(x => nameof(x.PutSchemaSync), values));
AddPutLink("update/urls", resources.Url<SchemasController>(x => nameof(x.PutPreviewUrls), values));
AddPutLink("update/rules", resources.Url<SchemasController>(x => nameof(x.PutRules), values));
AddPutLink("update/category", resources.Url<SchemasController>(x => nameof(x.PutCategory), values));
}
if (resources.CanUpdateSchemaScripts(Name))

13
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterCompareTests.cs

@ -34,7 +34,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
{
public class RuleEventFormatterCompareTests
{
private readonly IUser user = A.Fake<IUser>();
private readonly IUser user = UserMocks.User("user123", "me@email.com", "me");
private readonly IUrlGenerator urlGenerator = A.Fake<IUrlGenerator>();
private readonly NamedId<DomainId> appId = NamedId.Of(DomainId.NewGuid(), "my-app");
private readonly NamedId<DomainId> schemaId = NamedId.Of(DomainId.NewGuid(), "my-schema");
@ -74,15 +74,6 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
A.CallTo(() => urlGenerator.AssetContent(appId, "file-name"))
.Returns("asset-content-slug-url");
A.CallTo(() => user.Id)
.Returns("user123");
A.CallTo(() => user.Email)
.Returns("me@email.com");
A.CallTo(() => user.Claims)
.Returns(new List<Claim> { new Claim(SquidexClaimTypes.DisplayName, "me") });
var formatters = new IRuleEventFormatter[]
{
new PredefinedPatternsFormatter(urlGenerator),
@ -247,7 +238,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var result = await sut.FormatAsync(script, @event);
Assert.Equal("From client:android (client:android, android)", result);
Assert.Equal("From android (client:android, android)", result);
}
[Theory]

11
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs

@ -32,7 +32,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
{
public class RuleEventFormatterTests
{
private readonly IUser user = A.Fake<IUser>();
private readonly IUser user = UserMocks.User("user123", "me@email.com", "me");
private readonly IUrlGenerator urlGenerator = A.Fake<IUrlGenerator>();
private readonly NamedId<DomainId> appId = NamedId.Of(DomainId.NewGuid(), "my-app");
private readonly NamedId<DomainId> schemaId = NamedId.Of(DomainId.NewGuid(), "my-schema");
@ -68,15 +68,6 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
A.CallTo(() => urlGenerator.AssetContent(appId, assetId.ToString()))
.Returns("asset-content-url");
A.CallTo(() => user.Id)
.Returns("user123");
A.CallTo(() => user.Email)
.Returns("me@email.com");
A.CallTo(() => user.Claims)
.Returns(new List<Claim> { new Claim(SquidexClaimTypes.DisplayName, "me") });
var formatters = new IRuleEventFormatter[]
{
new PredefinedPatternsFormatter(urlGenerator),

46
backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/UserMocks.cs

@ -0,0 +1,46 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Security.Claims;
using FakeItEasy;
using Squidex.Shared.Identity;
using Squidex.Shared.Users;
namespace Squidex.Domain.Apps.Core.TestHelpers
{
public static class UserMocks
{
public static IUser User(string id, string? email = null, string? name = null, bool consent = false)
{
var claims = new List<Claim>();
if (!string.IsNullOrWhiteSpace(name))
{
claims.Add(new Claim(SquidexClaimTypes.DisplayName, name));
}
if (consent)
{
claims.Add(new Claim(SquidexClaimTypes.Consent, "True"));
}
var user = A.Fake<IUser>();
A.CallTo(() => user.Id)
.Returns(id);
A.CallTo(() => user.Email)
.Returns(email ?? id);
A.CallTo(() => user.Claims)
.Returns(claims);
return user;
}
}
}

6
backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppDomainObjectTests.cs

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.Plans;
using Squidex.Domain.Apps.Entities.TestHelpers;
@ -26,7 +27,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject
{
private readonly IAppPlansProvider appPlansProvider = A.Fake<IAppPlansProvider>();
private readonly IAppPlanBillingManager appPlansBillingManager = A.Fake<IAppPlanBillingManager>();
private readonly IUser user = A.Fake<IUser>();
private readonly IUser user;
private readonly IUserResolver userResolver = A.Fake<IUserResolver>();
private readonly string contributorId = DomainId.NewGuid().ToString();
private readonly string clientId = "client";
@ -48,8 +49,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject
public AppDomainObjectTests()
{
A.CallTo(() => user.Id)
.Returns(contributorId);
user = UserMocks.User(contributorId);
A.CallTo(() => userResolver.FindByIdOrEmailAsync(contributorId))
.Returns(user);

6
backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/Guards/GuardAppContributorsTests.cs

@ -23,9 +23,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject.Guards
{
public class GuardAppContributorsTests : IClassFixture<TranslationsFixture>
{
private readonly IUser user1 = A.Fake<IUser>();
private readonly IUser user2 = A.Fake<IUser>();
private readonly IUser user3 = A.Fake<IUser>();
private readonly IUser user1 = UserMocks.User("1");
private readonly IUser user2 = UserMocks.User("2");
private readonly IUser user3 = UserMocks.User("3");
private readonly IUserResolver users = A.Fake<IUserResolver>();
private readonly IAppLimitsPlan appPlan = A.Fake<IAppLimitsPlan>();
private readonly AppContributors contributors_0 = AppContributors.Empty;

5
backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/InvitationEventConsumerTests.cs

@ -9,6 +9,7 @@ using System;
using System.Threading.Tasks;
using FakeItEasy;
using NodaTime;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Notifications;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Infrastructure;
@ -23,8 +24,8 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation
{
private readonly INotificationSender notificatíonSender = A.Fake<INotificationSender>();
private readonly IUserResolver userResolver = A.Fake<IUserResolver>();
private readonly IUser assigner = A.Fake<IUser>();
private readonly IUser assignee = A.Fake<IUser>();
private readonly IUser assigner = UserMocks.User("1");
private readonly IUser assignee = UserMocks.User("2");
private readonly ISemanticLog log = A.Fake<ISemanticLog>();
private readonly string assignerId = DomainId.NewGuid().ToString();
private readonly string assigneeId = DomainId.NewGuid().ToString();

22
backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/InviteUserCommandMiddlewareTests.cs

@ -7,6 +7,7 @@
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure;
@ -37,9 +38,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation
new CommandContext(command, commandBus)
.Complete(app);
var user = CreateUser("123");
var user = UserMocks.User("123", command.ContributorId);
A.CallTo(() => userResolver.CreateUserIfNotExistsAsync("me@email.com", true))
A.CallTo(() => userResolver.CreateUserIfNotExistsAsync(user.Email, true))
.Returns((user, true));
await sut.HandleAsync(context);
@ -47,7 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation
Assert.Same(context.Result<InvitedResult>().App, app);
Assert.Equal(user.Id, command.ContributorId);
A.CallTo(() => userResolver.CreateUserIfNotExistsAsync("me@email.com", true))
A.CallTo(() => userResolver.CreateUserIfNotExistsAsync(user.Email, true))
.MustHaveHappened();
}
@ -60,9 +61,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation
new CommandContext(command, commandBus)
.Complete(app);
var user = CreateUser("123");
var user = UserMocks.User("123", command.ContributorId);
A.CallTo(() => userResolver.CreateUserIfNotExistsAsync("me@email.com", true))
A.CallTo(() => userResolver.CreateUserIfNotExistsAsync(user.Email, true))
.Returns((user, false));
await sut.HandleAsync(context);
@ -70,7 +71,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation
Assert.Same(context.Result<IAppEntity>(), app);
Assert.Equal(user.Id, command.ContributorId);
A.CallTo(() => userResolver.CreateUserIfNotExistsAsync("me@email.com", true))
A.CallTo(() => userResolver.CreateUserIfNotExistsAsync(user.Email, true))
.MustHaveHappened();
}
@ -103,14 +104,5 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation
A.CallTo(() => userResolver.CreateUserIfNotExistsAsync(A<string>._, A<bool>._))
.MustNotHaveHappened();
}
private static IUser CreateUser(string id)
{
var user = A.Fake<IUser>();
A.CallTo(() => user.Id).Returns(id);
return user;
}
}
}

6
backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Plans/UsageNotifierGrainTests.cs

@ -8,6 +8,7 @@
using System.Threading.Tasks;
using FakeItEasy;
using NodaTime;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Notifications;
using Squidex.Infrastructure.Orleans;
using Squidex.Shared.Users;
@ -157,10 +158,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Plans
{
if (email != null)
{
var user = A.Fake<IUser>();
A.CallTo(() => user.Email)
.Returns(email);
var user = UserMocks.User(id, email);
A.CallTo(() => userResolver.FindByIdOrEmailAsync(id))
.Returns(user);

30
backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/UserMappingTests.cs

@ -29,13 +29,13 @@ namespace Squidex.Domain.Apps.Entities.Backup
[Fact]
public async Task Should_backup_users_but_no_clients()
{
sut.Backup("user1");
sut.Backup(Subject("user2"));
sut.Backup("1");
sut.Backup(Subject("2"));
sut.Backup(Client("client"));
var user1 = CreateUser("user1", "mail1@squidex.io");
var user2 = CreateUser("user2", "mail2@squidex.io");
var user1 = UserMocks.User("1", "1@email.com");
var user2 = UserMocks.User("2", "1@email.com");
var users = new Dictionary<string, IUser>
{
@ -67,8 +67,8 @@ namespace Squidex.Domain.Apps.Entities.Backup
[Fact]
public async Task Should_restore_users()
{
var user1 = CreateUser("user1", "mail1@squidex.io");
var user2 = CreateUser("user2", "mail2@squidex.io");
var user1 = UserMocks.User("1", "1@email.com");
var user2 = UserMocks.User("2", "2@email.com");
var reader = SetupReader(user1, user2);
@ -82,11 +82,11 @@ namespace Squidex.Domain.Apps.Entities.Backup
await sut.RestoreAsync(reader, userResolver);
Assert.True(sut.TryMap("user1_old", out var mapped1));
Assert.True(sut.TryMap(Subject("user2_old"), out var mapped2));
Assert.True(sut.TryMap("1_old", out var mapped1));
Assert.True(sut.TryMap(Subject("2_old"), out var mapped2));
Assert.Equal(Subject("user1"), mapped1);
Assert.Equal(Subject("user2"), mapped2);
Assert.Equal(Subject("1"), mapped1);
Assert.Equal(Subject("2"), mapped2);
}
[Fact]
@ -107,16 +107,6 @@ namespace Squidex.Domain.Apps.Entities.Backup
Assert.Same(client, mapped);
}
private static IUser CreateUser(string id, string email)
{
var user = A.Fake<IUser>();
A.CallTo(() => user.Id).Returns(id);
A.CallTo(() => user.Email).Returns(email);
return user;
}
private static IBackupReader SetupReader(params IUser[] users)
{
var storedUsers = users.ToDictionary(x => $"{x.Id}_old", x => x.Email);

25
backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/CommentTriggerHandlerTests.cs

@ -16,6 +16,7 @@ using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules.EnrichedEvents;
using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Comments;
using Squidex.Domain.Apps.Events.Contents;
@ -52,8 +53,8 @@ namespace Squidex.Domain.Apps.Entities.Comments
[Fact]
public async Task Should_create_enriched_events()
{
var user1 = CreateUser("1");
var user2 = CreateUser("2");
var user1 = UserMocks.User("1");
var user2 = UserMocks.User("2");
var users = new List<IUser> { user1, user2 };
var userIds = users.Select(x => x.Id).ToArray();
@ -79,8 +80,8 @@ namespace Squidex.Domain.Apps.Entities.Comments
[Fact]
public async Task Should_not_create_enriched_events_when_users_cannot_be_resolved()
{
var user1 = CreateUser("1");
var user2 = CreateUser("2");
var user1 = UserMocks.User("1");
var user2 = UserMocks.User("2");
var users = new List<IUser> { user1, user2 };
var userIds = users.Select(x => x.Id).ToArray();
@ -213,9 +214,9 @@ namespace Squidex.Domain.Apps.Entities.Comments
[Fact]
public void Should_trigger_check_when_email_is_correct()
{
TestForRealCondition("event.mentionedUser.email == 'sebastian@squidex.io'", (handler, trigger) =>
TestForRealCondition("event.mentionedUser.email == '1@email.com'", (handler, trigger) =>
{
var user = CreateUser("1");
var user = UserMocks.User("1", "1@email.com");
var result = handler.Trigger(new EnrichedCommentEvent { MentionedUser = user }, trigger);
@ -228,7 +229,7 @@ namespace Squidex.Domain.Apps.Entities.Comments
{
TestForRealCondition("event.mentionedUser.email == 'other@squidex.io'", (handler, trigger) =>
{
var user = CreateUser("1");
var user = UserMocks.User("1");
var result = handler.Trigger(new EnrichedCommentEvent { MentionedUser = user }, trigger);
@ -262,16 +263,6 @@ namespace Squidex.Domain.Apps.Entities.Comments
});
}
private static IUser CreateUser(string id, string email = "sebastian@squidex.io")
{
var user = A.Fake<IUser>();
A.CallTo(() => user.Id).Returns(id);
A.CallTo(() => user.Email).Returns(email);
return user;
}
private void TestForRealCondition(string condition, Action<IRuleTriggerHandler, CommentTrigger> action)
{
var trigger = new CommentTrigger

6
backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/DomainObject/CommentsCommandMiddlewareTests.cs

@ -8,6 +8,7 @@
using System.Threading.Tasks;
using FakeItEasy;
using Orleans;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Comments.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
@ -138,10 +139,7 @@ namespace Squidex.Domain.Apps.Entities.Comments.DomainObject
private void SetupUser(string id, string email)
{
var user = A.Fake<IUser>();
A.CallTo(() => user.Id).Returns(id);
A.CallTo(() => user.Email).Returns(email);
var user = UserMocks.User(id, email);
A.CallTo(() => userResolver.FindByIdOrEmailAsync(email))
.Returns(user);

2
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs

@ -229,7 +229,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
created = content.Created,
createdBy = "subject:user1",
lastModified = content.LastModified,
lastModifiedBy = "subject:user2",
lastModifiedBy = "client:client1",
status = "DRAFT",
statusColor = "red",
url = $"contents/my-schema/{content.Id}",

14
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs

@ -6,6 +6,7 @@
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using FakeItEasy;
using GraphQL;
@ -32,6 +33,7 @@ using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Json;
using Squidex.Log;
using Squidex.Shared;
using Squidex.Shared.Users;
using Xunit;
#pragma warning disable SA1401 // Fields must be private
@ -46,6 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
protected readonly IAssetQueryService assetQuery = A.Fake<IAssetQueryService>();
protected readonly ICommandBus commandBus = A.Fake<ICommandBus>();
protected readonly IContentQueryService contentQuery = A.Fake<IContentQueryService>();
protected readonly IUserResolver userResolver = A.Fake<IUserResolver>();
protected readonly ISchemaEntity schema;
protected readonly ISchemaEntity schemaRef1;
protected readonly ISchemaEntity schemaRef2;
@ -63,6 +66,16 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
app = Mocks.App(appId, Language.DE, Language.GermanGermany);
A.CallTo(() => userResolver.QueryManyAsync(A<string[]>._))
.ReturnsLazily(x =>
{
var ids = x.GetArgument<string[]>(0)!;
var users = ids.Select(id => UserMocks.User(id, $"{id}@email.com", $"name_{id}"));
return Task.FromResult(users.ToDictionary(x => x.Id));
});
var schemaDef =
new Schema(schemaId.Name)
.Publish()
@ -201,6 +214,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
.AddSingleton(contentQuery)
.AddSingleton(dataLoaderContext)
.AddSingleton(dataLoaderListener)
.AddSingleton(userResolver)
.AddSingleton<InstantGraphType>()
.AddSingleton<JsonGraphType>()
.AddSingleton<JsonNoopGraphType>()

24
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestAsset.cs

@ -20,8 +20,18 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
version
created
createdBy
createdByUser {
id,
email,
displayName
}
lastModified
lastModifiedBy
lastModifiedByUser {
id,
email,
displayName
}
url
thumbnailUrl
sourceUrl
@ -54,7 +64,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
Created = now,
CreatedBy = RefToken.User("user1"),
LastModified = now,
LastModifiedBy = RefToken.User("user2"),
LastModifiedBy = RefToken.Client("client1"),
FileName = "MyFile.png",
Slug = "myfile.png",
FileSize = 1024,
@ -85,8 +95,20 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
version = asset.Version,
created = asset.Created,
createdBy = asset.CreatedBy.ToString(),
createdByUser = new
{
id = asset.CreatedBy.Identifier,
email = $"{asset.CreatedBy.Identifier}@email.com",
displayName = $"name_{asset.CreatedBy.Identifier}"
},
lastModified = asset.LastModified,
lastModifiedBy = asset.LastModifiedBy.ToString(),
lastModifiedByUser = new
{
id = asset.LastModifiedBy.Identifier,
email = $"{asset.LastModifiedBy}",
displayName = asset.LastModifiedBy.Identifier
},
url = $"assets/{asset.AppId.Name}/{asset.Id}",
thumbnailUrl = $"assets/{asset.AppId.Name}/{asset.Id}?width=100",
sourceUrl = $"assets/source/{asset.Id}",

28
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestContent.cs

@ -20,8 +20,18 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
version
created
createdBy
createdByUser {
id,
email,
displayName
}
lastModified
lastModifiedBy
lastModifiedByUser {
id,
email,
displayName
}
status
statusColor
url
@ -148,7 +158,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
Created = now,
CreatedBy = RefToken.User("user1"),
LastModified = now,
LastModifiedBy = RefToken.User("user2"),
LastModifiedBy = RefToken.Client("client1"),
Data = data,
SchemaId = schemaId,
Status = Status.Draft,
@ -192,9 +202,21 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
id = content.Id,
version = 1,
created = content.Created,
createdBy = "subject:user1",
createdBy = content.CreatedBy.ToString(),
createdByUser = new
{
id = content.CreatedBy.Identifier,
email = $"{content.CreatedBy.Identifier}@email.com",
displayName = $"name_{content.CreatedBy.Identifier}"
},
lastModified = content.LastModified,
lastModifiedBy = "subject:user2",
lastModifiedBy = content.LastModifiedBy.ToString(),
lastModifiedByUser = new
{
id = content.LastModifiedBy.Identifier,
email = $"{content.LastModifiedBy}",
displayName = content.LastModifiedBy.Identifier
},
status = "DRAFT",
statusColor = "red",
url = $"contents/my-schema/{content.Id}",

68
backend/tests/Squidex.Domain.Apps.Entities.Tests/Notifications/NotificationEmailSenderTests.cs

@ -6,16 +6,14 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using FakeItEasy;
using Microsoft.Extensions.Options;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Infrastructure.Email;
using Squidex.Log;
using Squidex.Shared.Identity;
using Squidex.Shared.Users;
using Xunit;
@ -25,11 +23,9 @@ namespace Squidex.Domain.Apps.Entities.Notifications
{
private readonly IEmailSender emailSender = A.Fake<IEmailSender>();
private readonly IUrlGenerator urlGenerator = A.Fake<IUrlGenerator>();
private readonly IUser assigner = A.Fake<IUser>();
private readonly IUser user = A.Fake<IUser>();
private readonly IUser assigner = UserMocks.User("1", "1@email.com", "user1");
private readonly IUser assigned = UserMocks.User("2", "2@email.com", "user2");
private readonly ISemanticLog log = A.Fake<ISemanticLog>();
private readonly List<Claim> assignerClaims = new List<Claim> { new Claim(SquidexClaimTypes.DisplayName, "Sebastian Stehle") };
private readonly List<Claim> assigneeClaims = new List<Claim> { new Claim(SquidexClaimTypes.DisplayName, "Qaisar Ahmad") };
private readonly string appName = "my-app";
private readonly string uiUrl = "my-ui";
private readonly NotificationEmailTextOptions texts = new NotificationEmailTextOptions();
@ -37,16 +33,6 @@ namespace Squidex.Domain.Apps.Entities.Notifications
public NotificationEmailSenderTests()
{
A.CallTo(() => assigner.Email)
.Returns("sebastian@squidex.io");
A.CallTo(() => assigner.Claims)
.Returns(assignerClaims);
A.CallTo(() => user.Email)
.Returns("qaisar@squidex.io");
A.CallTo(() => user.Claims)
.Returns(assigneeClaims);
A.CallTo(() => urlGenerator.UI())
.Returns(uiUrl);
@ -56,25 +42,25 @@ namespace Squidex.Domain.Apps.Entities.Notifications
[Fact]
public async Task Should_format_assigner_email_and_send_email()
{
await TestInvitationFormattingAsync("Email: $ASSIGNER_EMAIL", "Email: sebastian@squidex.io");
await TestInvitationFormattingAsync("Email: $ASSIGNER_EMAIL", "Email: 1@email.com");
}
[Fact]
public async Task Should_format_assigner_name_and_send_email()
{
await TestInvitationFormattingAsync("Name: $ASSIGNER_NAME", "Name: Sebastian Stehle");
await TestInvitationFormattingAsync("Name: $ASSIGNER_NAME", "Name: user1");
}
[Fact]
public async Task Should_format_user_email_and_send_email()
{
await TestInvitationFormattingAsync("Email: $USER_EMAIL", "Email: qaisar@squidex.io");
await TestInvitationFormattingAsync("Email: $USER_EMAIL", "Email: 2@email.com");
}
[Fact]
public async Task Should_format_user_name_and_send_email()
{
await TestInvitationFormattingAsync("Name: $USER_NAME", "Name: Qaisar Ahmad");
await TestInvitationFormattingAsync("Name: $USER_NAME", "Name: user2");
}
[Fact]
@ -104,9 +90,9 @@ namespace Squidex.Domain.Apps.Entities.Notifications
[Fact]
public async Task Should_not_send_invitation_email_if_texts_for_new_user_are_empty()
{
await sut.SendInviteAsync(assigner, user, appName);
await sut.SendInviteAsync(assigner, assigned, appName);
A.CallTo(() => emailSender.SendAsync(user.Email, A<string>._, A<string>._, A<CancellationToken>._))
A.CallTo(() => emailSender.SendAsync(assigned.Email, A<string>._, A<string>._, A<CancellationToken>._))
.MustNotHaveHappened();
MustLogWarning();
@ -115,9 +101,9 @@ namespace Squidex.Domain.Apps.Entities.Notifications
[Fact]
public async Task Should_not_send_invitation_email_if_texts_for_existing_user_are_empty()
{
await sut.SendInviteAsync(assigner, user, appName);
await sut.SendInviteAsync(assigner, assigned, appName);
A.CallTo(() => emailSender.SendAsync(user.Email, A<string>._, A<string>._, A<CancellationToken>._))
A.CallTo(() => emailSender.SendAsync(assigned.Email, A<string>._, A<string>._, A<CancellationToken>._))
.MustNotHaveHappened();
MustLogWarning();
@ -126,25 +112,39 @@ namespace Squidex.Domain.Apps.Entities.Notifications
[Fact]
public async Task Should_not_send_usage_email_if_texts_empty()
{
await sut.SendUsageAsync(user, appName, 100, 120);
await sut.SendUsageAsync(assigned, appName, 100, 120);
A.CallTo(() => emailSender.SendAsync(user.Email, A<string>._, A<string>._, A<CancellationToken>._))
A.CallTo(() => emailSender.SendAsync(assigned.Email, A<string>._, A<string>._, A<CancellationToken>._))
.MustNotHaveHappened();
MustLogWarning();
}
[Fact]
public async Task Should_not_send_invitation_email_when_no_consent_given()
{
var withoutConsent = UserMocks.User("2", "2@email.com", "user", false);
texts.ExistingUserSubject = "email-subject";
texts.ExistingUserBody = "email-body";
await sut.SendInviteAsync(assigner, withoutConsent, appName);
A.CallTo(() => emailSender.SendAsync(withoutConsent.Email, "email-subject", "email-body", A<CancellationToken>._))
.MustNotHaveHappened();
}
[Fact]
public async Task Should_send_invitation_email_when_consent_given()
{
assigneeClaims.Add(new Claim(SquidexClaimTypes.Consent, "True"));
var withConsent = UserMocks.User("2", "2@email.com", "user", true);
texts.ExistingUserSubject = "email-subject";
texts.ExistingUserBody = "email-body";
await sut.SendInviteAsync(assigner, user, appName);
await sut.SendInviteAsync(assigner, withConsent, appName);
A.CallTo(() => emailSender.SendAsync(user.Email, "email-subject", "email-body", A<CancellationToken>._))
A.CallTo(() => emailSender.SendAsync(withConsent.Email, "email-subject", "email-body", A<CancellationToken>._))
.MustHaveHappened();
}
@ -153,9 +153,9 @@ namespace Squidex.Domain.Apps.Entities.Notifications
texts.UsageSubject = pattern;
texts.UsageBody = pattern;
await sut.SendUsageAsync(user, appName, 100, 120);
await sut.SendUsageAsync(assigned, appName, 100, 120);
A.CallTo(() => emailSender.SendAsync(user.Email, result, result, A<CancellationToken>._))
A.CallTo(() => emailSender.SendAsync(assigned.Email, result, result, A<CancellationToken>._))
.MustHaveHappened();
}
@ -164,9 +164,9 @@ namespace Squidex.Domain.Apps.Entities.Notifications
texts.NewUserSubject = pattern;
texts.NewUserBody = pattern;
await sut.SendInviteAsync(assigner, user, appName);
await sut.SendInviteAsync(assigner, assigned, appName);
A.CallTo(() => emailSender.SendAsync(user.Email, result, result, A<CancellationToken>._))
A.CallTo(() => emailSender.SendAsync(assigned.Email, result, result, A<CancellationToken>._))
.MustHaveHappened();
}

30
backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/Mocks.cs

@ -31,10 +31,17 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers
var app = A.Fake<IAppEntity>();
A.CallTo(() => app.Id).Returns(appId.Id);
A.CallTo(() => app.Name).Returns(appId.Name);
A.CallTo(() => app.Languages).Returns(config);
A.CallTo(() => app.UniqueId).Returns(appId.Id);
A.CallTo(() => app.Id)
.Returns(appId.Id);
A.CallTo(() => app.Name)
.Returns(appId.Name);
A.CallTo(() => app.Languages)
.Returns(config);
A.CallTo(() => app.UniqueId)
.Returns(appId.Id);
return app;
}
@ -43,10 +50,17 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers
{
var schema = A.Fake<ISchemaEntity>();
A.CallTo(() => schema.Id).Returns(schemaId.Id);
A.CallTo(() => schema.AppId).Returns(appId);
A.CallTo(() => schema.SchemaDef).Returns(schemaDef ?? new Schema(schemaId.Name));
A.CallTo(() => schema.UniqueId).Returns(DomainId.Combine(appId, schemaId.Id));
A.CallTo(() => schema.Id)
.Returns(schemaId.Id);
A.CallTo(() => schema.AppId)
.Returns(appId);
A.CallTo(() => schema.SchemaDef)
.Returns(schemaDef ?? new Schema(schemaId.Name));
A.CallTo(() => schema.UniqueId)
.Returns(DomainId.Combine(appId, schemaId.Id));
return schema;
}

Loading…
Cancel
Save