Browse Source

Optimize asset folder queries.

pull/751/head
Sebastian 4 years ago
parent
commit
9390c3c57e
  1. 29
      backend/src/Squidex/Areas/Api/Controllers/Assets/AssetFoldersController.cs
  2. 16
      backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetFolderScope.cs
  3. 2
      frontend/app/shared/components/assets/asset-dialog.component.ts
  4. 2
      frontend/app/shared/components/assets/asset-folder-dropdown-item.component.ts
  5. 2
      frontend/app/shared/components/assets/asset-folder-dropdown.component.ts
  6. 4
      frontend/app/shared/services/assets.service.spec.ts
  7. 5
      frontend/app/shared/services/assets.service.ts

29
backend/src/Squidex/Areas/Api/Controllers/Assets/AssetFoldersController.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@ -13,6 +14,7 @@ using Squidex.Areas.Api.Controllers.Assets.Models;
using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Tasks;
using Squidex.Shared;
@ -39,6 +41,7 @@ namespace Squidex.Areas.Api.Controllers.Assets
/// </summary>
/// <param name="app">The name of the app.</param>
/// <param name="parentId">The optional parent folder id.</param>
/// <param name="scope">The scope of the query.</param>
/// <returns>
/// 200 => Asset folders returned.
/// 404 => App not found.
@ -51,11 +54,11 @@ namespace Squidex.Areas.Api.Controllers.Assets
[ProducesResponseType(typeof(AssetFoldersDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppAssetsRead)]
[ApiCosts(1)]
public async Task<IActionResult> GetAssetFolders(string app, [FromQuery] DomainId parentId)
public async Task<IActionResult> GetAssetFolders(string app, [FromQuery] DomainId parentId, [FromQuery] AssetFolderScope scope = AssetFolderScope.PathAndItems)
{
var (folders, path) = await AsyncHelper.WhenAll(
assetQuery.QueryAssetFoldersAsync(Context, parentId, HttpContext.RequestAborted),
assetQuery.FindAssetFolderAsync(Context.App.Id, parentId, HttpContext.RequestAborted));
GetAssetFoldersAsync(parentId, scope),
GetAssetPathAsync(parentId, scope));
var response = Deferred.Response(() =>
{
@ -170,5 +173,25 @@ namespace Squidex.Areas.Api.Controllers.Assets
return AssetFolderDto.FromAssetFolder(context.Result<IAssetFolderEntity>(), Resources);
}
private Task<IReadOnlyList<IAssetFolderEntity>> GetAssetPathAsync(DomainId parentId, AssetFolderScope scope)
{
if (scope == AssetFolderScope.Items)
{
return Task.FromResult<IReadOnlyList<IAssetFolderEntity>>(ImmutableList.Empty<IAssetFolderEntity>());
}
return assetQuery.FindAssetFolderAsync(Context.App.Id, parentId, HttpContext.RequestAborted);
}
private Task<IResultList<IAssetFolderEntity>> GetAssetFoldersAsync(DomainId parentId, AssetFolderScope scope)
{
if (scope == AssetFolderScope.Path)
{
return Task.FromResult(ResultList.CreateFrom<IAssetFolderEntity>(0));
}
return assetQuery.QueryAssetFoldersAsync(Context, parentId, HttpContext.RequestAborted);
}
}
}

16
backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetFolderScope.cs

@ -0,0 +1,16 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Areas.Api.Controllers.Assets.Models
{
public enum AssetFolderScope
{
PathAndItems,
Path,
Items
}
}

2
frontend/app/shared/components/assets/asset-dialog.component.ts

@ -89,7 +89,7 @@ export class AssetDialogComponent implements OnChanges {
this.annotateForm.setEnabled(this.isEditable);
this.path =
this.assetsService.getAssetFolders(this.appsState.appName, this.asset.parentId).pipe(
this.assetsService.getAssetFolders(this.appsState.appName, this.asset.parentId, 'Path').pipe(
map(folders => [ROOT_ITEM, ...folders.path]));
}

2
frontend/app/shared/components/assets/asset-folder-dropdown-item.component.ts

@ -61,7 +61,7 @@ export class AssetFolderDropdownItemComponent {
this.node.isLoading = true;
this.assetsService.getAssetFolders(this.appName, this.node.item.id)
this.assetsService.getAssetFolders(this.appName, this.node.item.id, 'Items')
.subscribe({
next: dto => {
if (dto.items.length > 0) {

2
frontend/app/shared/components/assets/asset-folder-dropdown.component.ts

@ -59,7 +59,7 @@ export class AssetFolderDropdownComponent extends StatefulControlComponent<any,
return;
}
this.assetsService.getAssetFolders(this.appName, obj)
this.assetsService.getAssetFolders(this.appName, obj, 'PathAndItems')
.subscribe(dto => {
let parent = this.root;

4
frontend/app/shared/services/assets.service.spec.ts

@ -91,11 +91,11 @@ describe('AssetsService', () => {
inject([AssetsService, HttpTestingController], (assetsService: AssetsService, httpMock: HttpTestingController) => {
let assets: AssetFoldersDto;
assetsService.getAssetFolders('my-app', 'parent1').subscribe(result => {
assetsService.getAssetFolders('my-app', 'parent1', 'Path').subscribe(result => {
assets = result;
});
const req = httpMock.expectOne('http://service/p/api/apps/my-app/assets/folders?parentId=parent1');
const req = httpMock.expectOne('http://service/p/api/apps/my-app/assets/folders?parentId=parent1&scope=Path');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull();

5
frontend/app/shared/services/assets.service.ts

@ -135,6 +135,7 @@ export class AssetFolderDto {
type Tags = readonly string[];
type AssetFolderScope = 'PathAndItems' | 'Path' | 'Items';
type AssetMetadata = { [key: string]: any };
export type AnnotateAssetDto =
@ -247,8 +248,8 @@ export class AssetsService {
}
}
public getAssetFolders(appName: string, parentId: string): Observable<AssetFoldersDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/assets/folders?parentId=${parentId}`);
public getAssetFolders(appName: string, parentId: string, scope: AssetFolderScope): Observable<AssetFoldersDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/assets/folders?parentId=${parentId}&scope=${scope}`);
return this.http.get<{ total: number; items: any[]; folders: any[]; path: any[] } & Resource>(url).pipe(
map(({ total, items, path, _links }) => {

Loading…
Cancel
Save