|
|
|
@ -31,11 +31,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries |
|
|
|
private readonly IContentRepository contentRepository = A.Fake<IContentRepository>(); |
|
|
|
private readonly IContentLoader contentVersionLoader = A.Fake<IContentLoader>(); |
|
|
|
private readonly ISchemaEntity schema; |
|
|
|
private readonly DomainId contentId = DomainId.NewGuid(); |
|
|
|
private readonly NamedId<DomainId> appId = NamedId.Of(DomainId.NewGuid(), "my-app"); |
|
|
|
private readonly NamedId<DomainId> schemaId = NamedId.Of(DomainId.NewGuid(), "my-schema"); |
|
|
|
private readonly ContentData contentData = new ContentData(); |
|
|
|
private readonly ContentData contentTransformed = new ContentData(); |
|
|
|
private readonly ContentQueryParser queryParser = A.Fake<ContentQueryParser>(); |
|
|
|
private readonly ContentQueryService sut; |
|
|
|
|
|
|
|
@ -67,66 +65,70 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public async Task GetSchemaOrThrowAsync_should_return_schema_from_guid_string() |
|
|
|
public async Task Should_get_schema_from_guid_string() |
|
|
|
{ |
|
|
|
var input = schemaId.Id.ToString(); |
|
|
|
|
|
|
|
var ctx = CreateContext(isFrontend: false, allowSchema: true); |
|
|
|
var requestContext = CreateContext(); |
|
|
|
|
|
|
|
A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, schemaId.Id, true)) |
|
|
|
.Returns(schema); |
|
|
|
|
|
|
|
var result = await sut.GetSchemaOrThrowAsync(ctx, input); |
|
|
|
var result = await sut.GetSchemaOrThrowAsync(requestContext, input); |
|
|
|
|
|
|
|
Assert.Equal(schema, result); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public async Task GetSchemaOrThrowAsync_should_return_schema_from_name() |
|
|
|
public async Task Should_get_schema_from_name() |
|
|
|
{ |
|
|
|
var input = schemaId.Name; |
|
|
|
|
|
|
|
var ctx = CreateContext(isFrontend: false, allowSchema: true); |
|
|
|
var requestContext = CreateContext(); |
|
|
|
|
|
|
|
A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, schemaId.Name, true)) |
|
|
|
.Returns(schema); |
|
|
|
|
|
|
|
var result = await sut.GetSchemaOrThrowAsync(ctx, input); |
|
|
|
var result = await sut.GetSchemaOrThrowAsync(requestContext, input); |
|
|
|
|
|
|
|
Assert.Equal(schema, result); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public async Task GetSchemaOrThrowAsync_should_throw_404_if_not_found() |
|
|
|
public async Task Should_throw_notfound_exception_if_schema_to_get_not_found() |
|
|
|
{ |
|
|
|
var ctx = CreateContext(isFrontend: false, allowSchema: true); |
|
|
|
var requestContext = CreateContext(); |
|
|
|
|
|
|
|
A.CallTo(() => appProvider.GetSchemaAsync(A<DomainId>._, A<string>._, true)) |
|
|
|
.Returns((ISchemaEntity?)null); |
|
|
|
|
|
|
|
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.GetSchemaOrThrowAsync(ctx, schemaId.Name)); |
|
|
|
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.GetSchemaOrThrowAsync(requestContext, schemaId.Name)); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public async Task FindContentAsync_should_throw_exception_if_user_has_no_permission() |
|
|
|
public async Task Should_throw_permission_exception_if_content_to_find_is_restricted() |
|
|
|
{ |
|
|
|
var ctx = CreateContext(isFrontend: false, allowSchema: false); |
|
|
|
var requestContext = CreateContext(allowSchema: false); |
|
|
|
|
|
|
|
A.CallTo(() => contentRepository.FindContentAsync(ctx.App, schema, contentId, A<SearchScope>._)) |
|
|
|
.Returns(CreateContent(contentId)); |
|
|
|
var content = CreateContent(DomainId.NewGuid()); |
|
|
|
|
|
|
|
await Assert.ThrowsAsync<DomainForbiddenException>(() => sut.FindAsync(ctx, schemaId.Name, contentId)); |
|
|
|
A.CallTo(() => contentRepository.FindContentAsync(requestContext.App, schema, content.Id, A<SearchScope>._)) |
|
|
|
.Returns(CreateContent(DomainId.NewGuid())); |
|
|
|
|
|
|
|
await Assert.ThrowsAsync<DomainForbiddenException>(() => sut.FindAsync(requestContext, schemaId.Name, content.Id)); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public async Task FindContentAsync_should_return_null_if_not_found() |
|
|
|
public async Task Should_return_null_if_content_by_id_dannot_be_found() |
|
|
|
{ |
|
|
|
var ctx = CreateContext(isFrontend: false, allowSchema: true); |
|
|
|
var requestContext = CreateContext(); |
|
|
|
|
|
|
|
var content = CreateContent(DomainId.NewGuid()); |
|
|
|
|
|
|
|
A.CallTo(() => contentRepository.FindContentAsync(ctx.App, schema, contentId, A<SearchScope>._)) |
|
|
|
A.CallTo(() => contentRepository.FindContentAsync(requestContext.App, schema, content.Id, A<SearchScope>._)) |
|
|
|
.Returns<IContentEntity?>(null); |
|
|
|
|
|
|
|
Assert.Null(await sut.FindAsync(ctx, schemaId.Name, contentId)); |
|
|
|
Assert.Null(await sut.FindAsync(requestContext, schemaId.Name, content.Id)); |
|
|
|
} |
|
|
|
|
|
|
|
[Theory] |
|
|
|
@ -134,45 +136,41 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries |
|
|
|
[InlineData(1, 1, SearchScope.All)] |
|
|
|
[InlineData(0, 1, SearchScope.All)] |
|
|
|
[InlineData(0, 0, SearchScope.Published)] |
|
|
|
public async Task FindContentAsync_should_return_content(int isFrontend, int unpublished, SearchScope scope) |
|
|
|
public async Task Should_return_content_by_id(int isFrontend, int unpublished, SearchScope scope) |
|
|
|
{ |
|
|
|
var ctx = |
|
|
|
CreateContext(isFrontend: isFrontend == 1, allowSchema: true) |
|
|
|
.WithUnpublished(unpublished == 1); |
|
|
|
var requestContext = CreateContext(isFrontend, isUnpublished: unpublished); |
|
|
|
|
|
|
|
var content = CreateContent(contentId); |
|
|
|
var content = CreateContent(DomainId.NewGuid()); |
|
|
|
|
|
|
|
A.CallTo(() => contentRepository.FindContentAsync(ctx.App, schema, contentId, scope)) |
|
|
|
A.CallTo(() => contentRepository.FindContentAsync(requestContext.App, schema, content.Id, scope)) |
|
|
|
.Returns(content); |
|
|
|
|
|
|
|
var result = await sut.FindAsync(ctx, schemaId.Name, contentId); |
|
|
|
var result = await sut.FindAsync(requestContext, schemaId.Name, content.Id); |
|
|
|
|
|
|
|
Assert.Equal(contentTransformed, result!.Data); |
|
|
|
Assert.Equal(content.Id, result.Id); |
|
|
|
AssertContent(content, result); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public async Task FindContentAsync_should_return_content_by_version() |
|
|
|
public async Task Should_return_content_by_id_and_version() |
|
|
|
{ |
|
|
|
var ctx = CreateContext(isFrontend: false, allowSchema: true); |
|
|
|
var requestContext = CreateContext(); |
|
|
|
|
|
|
|
var content = CreateContent(contentId); |
|
|
|
var content = CreateContent(DomainId.NewGuid()); |
|
|
|
|
|
|
|
A.CallTo(() => contentVersionLoader.GetAsync(appId.Id, contentId, 13)) |
|
|
|
A.CallTo(() => contentVersionLoader.GetAsync(appId.Id, content.Id, 13)) |
|
|
|
.Returns(content); |
|
|
|
|
|
|
|
var result = await sut.FindAsync(ctx, schemaId.Name, contentId, 13); |
|
|
|
var result = await sut.FindAsync(requestContext, schemaId.Name, content.Id, 13); |
|
|
|
|
|
|
|
Assert.Equal(contentTransformed, result!.Data); |
|
|
|
Assert.Equal(content.Id, result.Id); |
|
|
|
AssertContent(content, result); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public async Task QueryAsync_should_throw_if_user_has_no_permission() |
|
|
|
public async Task Should_throw_exception_if_user_has_no_permission_to_query_content() |
|
|
|
{ |
|
|
|
var ctx = CreateContext(isFrontend: false, allowSchema: false); |
|
|
|
var requestContext = CreateContext(allowSchema: false); |
|
|
|
|
|
|
|
await Assert.ThrowsAsync<DomainForbiddenException>(() => sut.QueryAsync(ctx, schemaId.Name, Q.Empty)); |
|
|
|
await Assert.ThrowsAsync<DomainForbiddenException>(() => sut.QueryAsync(requestContext, schemaId.Name, Q.Empty)); |
|
|
|
} |
|
|
|
|
|
|
|
[Theory] |
|
|
|
@ -180,89 +178,95 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries |
|
|
|
[InlineData(1, 1, SearchScope.All)] |
|
|
|
[InlineData(0, 1, SearchScope.All)] |
|
|
|
[InlineData(0, 0, SearchScope.Published)] |
|
|
|
public async Task QueryAsync_should_return_contents(int isFrontend, int unpublished, SearchScope scope) |
|
|
|
public async Task Should_query_contents(int isFrontend, int unpublished, SearchScope scope) |
|
|
|
{ |
|
|
|
var ctx = |
|
|
|
CreateContext(isFrontend: isFrontend == 1, allowSchema: true) |
|
|
|
.WithUnpublished(unpublished == 1); |
|
|
|
var requestContext = CreateContext(isFrontend, isUnpublished: unpublished); |
|
|
|
|
|
|
|
var content = CreateContent(contentId); |
|
|
|
var content1 = CreateContent(DomainId.NewGuid()); |
|
|
|
var content2 = CreateContent(DomainId.NewGuid()); |
|
|
|
|
|
|
|
var q = Q.Empty.WithReference(DomainId.NewGuid()); |
|
|
|
|
|
|
|
A.CallTo(() => contentRepository.QueryAsync(ctx.App, schema, q, scope)) |
|
|
|
.Returns(ResultList.CreateFrom(5, content)); |
|
|
|
|
|
|
|
var result = await sut.QueryAsync(ctx, schemaId.Name, q); |
|
|
|
A.CallTo(() => contentRepository.QueryAsync(requestContext.App, schema, q, scope)) |
|
|
|
.Returns(ResultList.CreateFrom(5, content1, content2)); |
|
|
|
|
|
|
|
Assert.Equal(contentData, result[0].Data); |
|
|
|
Assert.Equal(contentId, result[0].Id); |
|
|
|
var result = await sut.QueryAsync(requestContext, schemaId.Name, q); |
|
|
|
|
|
|
|
Assert.Equal(5, result.Total); |
|
|
|
|
|
|
|
AssertContent(content1, result[0]); |
|
|
|
AssertContent(content2, result[1]); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public async Task QueryAll_should_not_return_contents_if_user_has_no_permission() |
|
|
|
[Theory] |
|
|
|
[InlineData(1, 0, SearchScope.All)] |
|
|
|
[InlineData(1, 1, SearchScope.All)] |
|
|
|
[InlineData(0, 1, SearchScope.All)] |
|
|
|
[InlineData(0, 0, SearchScope.Published)] |
|
|
|
public async Task Should_query_contents_by_ids(int isFrontend, int unpublished, SearchScope scope) |
|
|
|
{ |
|
|
|
var ctx = CreateContext(isFrontend: false, allowSchema: false); |
|
|
|
var requestContext = CreateContext(isFrontend, isUnpublished: unpublished); |
|
|
|
|
|
|
|
var ids = Enumerable.Range(0, 5).Select(x => DomainId.NewGuid()).ToList(); |
|
|
|
|
|
|
|
var contents = ids.Select(CreateContent).ToList(); |
|
|
|
|
|
|
|
var q = Q.Empty.WithIds(ids); |
|
|
|
|
|
|
|
A.CallTo(() => contentRepository.QueryAsync(ctx.App, A<List<ISchemaEntity>>.That.Matches(x => x.Count == 0), q, SearchScope.All)) |
|
|
|
.Returns(ResultList.Create(0, ids.Select(CreateContent))); |
|
|
|
A.CallTo(() => contentRepository.QueryAsync(requestContext.App, |
|
|
|
A<List<ISchemaEntity>>.That.Matches(x => x.Count == 1), q, scope)) |
|
|
|
.Returns(ResultList.Create(5, contents)); |
|
|
|
|
|
|
|
var result = await sut.QueryAsync(ctx, q); |
|
|
|
var result = await sut.QueryAsync(requestContext, q); |
|
|
|
|
|
|
|
Assert.Empty(result); |
|
|
|
Assert.Equal(5, result.Total); |
|
|
|
|
|
|
|
for (var i = 0; i < contents.Count; i++) |
|
|
|
{ |
|
|
|
AssertContent(contents[i], result[i]); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public async Task QueryAll_should_only_query_only_users_contents_if_no_permission() |
|
|
|
public async Task Should_query_contents_with_matching_permissions() |
|
|
|
{ |
|
|
|
var ctx = |
|
|
|
CreateContext(true, true, Permissions.AppContentsReadOwn); |
|
|
|
var requestContext = CreateContext(allowSchema: false); |
|
|
|
|
|
|
|
await sut.QueryAsync(ctx, schemaId.Name, Q.Empty); |
|
|
|
var ids = Enumerable.Range(0, 5).Select(x => DomainId.NewGuid()).ToList(); |
|
|
|
|
|
|
|
A.CallTo(() => contentRepository.QueryAsync(ctx.App, schema, A<Q>.That.Matches(x => x.CreatedBy!.Equals(ctx.User.Token())), SearchScope.All)) |
|
|
|
.MustHaveHappened(); |
|
|
|
var q = Q.Empty.WithIds(ids); |
|
|
|
|
|
|
|
A.CallTo(() => contentRepository.QueryAsync(requestContext.App, |
|
|
|
A<List<ISchemaEntity>>.That.Matches(x => x.Count == 0), q, SearchScope.All)) |
|
|
|
.Returns(ResultList.Create(0, ids.Select(CreateContent))); |
|
|
|
|
|
|
|
var result = await sut.QueryAsync(requestContext, q); |
|
|
|
|
|
|
|
Assert.Empty(result); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public async Task QueryAll_should_query_all_contents_if_user_has_permission() |
|
|
|
public async Task Should_query_contents_from_user_if_user_has_only_own_permission() |
|
|
|
{ |
|
|
|
var ctx = |
|
|
|
CreateContext(true, true, Permissions.AppContentsRead); |
|
|
|
var requestContext = CreateContext(permissionId: Permissions.AppContentsReadOwn); |
|
|
|
|
|
|
|
await sut.QueryAsync(ctx, schemaId.Name, Q.Empty); |
|
|
|
await sut.QueryAsync(requestContext, schemaId.Name, Q.Empty); |
|
|
|
|
|
|
|
A.CallTo(() => contentRepository.QueryAsync(ctx.App, schema, A<Q>.That.Matches(x => x.CreatedBy == null), SearchScope.All)) |
|
|
|
A.CallTo(() => contentRepository.QueryAsync(requestContext.App, schema, |
|
|
|
A<Q>.That.Matches(x => Equals(x.CreatedBy, requestContext.User.Token())), SearchScope.Published)) |
|
|
|
.MustHaveHappened(); |
|
|
|
} |
|
|
|
|
|
|
|
[Theory] |
|
|
|
[InlineData(1, 0, SearchScope.All)] |
|
|
|
[InlineData(1, 1, SearchScope.All)] |
|
|
|
[InlineData(0, 1, SearchScope.All)] |
|
|
|
[InlineData(0, 0, SearchScope.Published)] |
|
|
|
public async Task QueryAll_should_return_contents(int isFrontend, int unpublished, SearchScope scope) |
|
|
|
[Fact] |
|
|
|
public async Task Should_query_all_contents_if_user_has_read_permission() |
|
|
|
{ |
|
|
|
var ctx = |
|
|
|
CreateContext(isFrontend: isFrontend == 1, allowSchema: true) |
|
|
|
.WithUnpublished(unpublished == 1); |
|
|
|
var requestContext = CreateContext(permissionId: Permissions.AppContentsRead); |
|
|
|
|
|
|
|
var ids = Enumerable.Range(0, 5).Select(x => DomainId.NewGuid()).ToList(); |
|
|
|
|
|
|
|
var q = Q.Empty.WithIds(ids); |
|
|
|
|
|
|
|
A.CallTo(() => contentRepository.QueryAsync(ctx.App, A<List<ISchemaEntity>>.That.Matches(x => x.Count == 1), q, scope)) |
|
|
|
.Returns(ResultList.Create(5, ids.Select(CreateContent))); |
|
|
|
|
|
|
|
var result = await sut.QueryAsync(ctx, q); |
|
|
|
await sut.QueryAsync(requestContext, schemaId.Name, Q.Empty); |
|
|
|
|
|
|
|
Assert.Equal(ids, result.Select(x => x.Id).ToList()); |
|
|
|
A.CallTo(() => contentRepository.QueryAsync(requestContext.App, schema, |
|
|
|
A<Q>.That.Matches(x => x.CreatedBy == null), SearchScope.Published)) |
|
|
|
.MustHaveHappened(); |
|
|
|
} |
|
|
|
|
|
|
|
private void SetupEnricher() |
|
|
|
@ -276,12 +280,18 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
private Context CreateContext(bool isFrontend, bool allowSchema, string permissionId = Permissions.AppContentsRead) |
|
|
|
private Context CreateContext( |
|
|
|
int isFrontend = 0, |
|
|
|
int isUnpublished = 0, |
|
|
|
bool allowSchema = true, |
|
|
|
string permissionId = Permissions.AppContentsRead) |
|
|
|
{ |
|
|
|
var claimsIdentity = new ClaimsIdentity(); |
|
|
|
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity); |
|
|
|
|
|
|
|
if (isFrontend) |
|
|
|
claimsIdentity.AddClaim(new Claim(OpenIdClaims.Subject, "user1")); |
|
|
|
|
|
|
|
if (isFrontend == 1) |
|
|
|
{ |
|
|
|
claimsIdentity.AddClaim(new Claim(OpenIdClaims.ClientId, DefaultClients.Frontend)); |
|
|
|
} |
|
|
|
@ -293,7 +303,15 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries |
|
|
|
claimsIdentity.AddClaim(new Claim(SquidexClaimTypes.Permissions, concretePermission)); |
|
|
|
} |
|
|
|
|
|
|
|
return new Context(claimsPrincipal, Mocks.App(appId)); |
|
|
|
return new Context(claimsPrincipal, Mocks.App(appId)).WithUnpublished(isUnpublished == 1); |
|
|
|
} |
|
|
|
|
|
|
|
private static void AssertContent(IContentEntity source, IEnrichedContentEntity? result) |
|
|
|
{ |
|
|
|
Assert.NotNull(result); |
|
|
|
Assert.NotSame(source, result); |
|
|
|
Assert.Same(source.Data, result?.Data); |
|
|
|
Assert.Equal(source.Id, result?.Id); |
|
|
|
} |
|
|
|
|
|
|
|
private IContentEntity CreateContent(DomainId id) |
|
|
|
|