From 9390c3c57e93f3abec64e0b1e99203c42e25d112 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 30 Aug 2021 20:43:23 +0200 Subject: [PATCH] Optimize asset folder queries. --- .../Assets/AssetFoldersController.cs | 29 +++++++++++++++++-- .../Assets/Models/AssetFolderScope.cs | 16 ++++++++++ .../assets/asset-dialog.component.ts | 2 +- .../asset-folder-dropdown-item.component.ts | 2 +- .../assets/asset-folder-dropdown.component.ts | 2 +- .../shared/services/assets.service.spec.ts | 4 +-- .../app/shared/services/assets.service.ts | 5 ++-- 7 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetFolderScope.cs diff --git a/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetFoldersController.cs b/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetFoldersController.cs index 7a18acd7c..22839c2ac 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetFoldersController.cs +++ b/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 /// /// The name of the app. /// The optional parent folder id. + /// The scope of the query. /// /// 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 GetAssetFolders(string app, [FromQuery] DomainId parentId) + public async Task 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(), Resources); } + + private Task> GetAssetPathAsync(DomainId parentId, AssetFolderScope scope) + { + if (scope == AssetFolderScope.Items) + { + return Task.FromResult>(ImmutableList.Empty()); + } + + return assetQuery.FindAssetFolderAsync(Context.App.Id, parentId, HttpContext.RequestAborted); + } + + private Task> GetAssetFoldersAsync(DomainId parentId, AssetFolderScope scope) + { + if (scope == AssetFolderScope.Path) + { + return Task.FromResult(ResultList.CreateFrom(0)); + } + + return assetQuery.QueryAssetFoldersAsync(Context, parentId, HttpContext.RequestAborted); + } } } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetFolderScope.cs b/backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetFolderScope.cs new file mode 100644 index 000000000..a99f152b3 --- /dev/null +++ b/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 + } +} diff --git a/frontend/app/shared/components/assets/asset-dialog.component.ts b/frontend/app/shared/components/assets/asset-dialog.component.ts index 9e1d4745d..64532d3b3 100644 --- a/frontend/app/shared/components/assets/asset-dialog.component.ts +++ b/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])); } diff --git a/frontend/app/shared/components/assets/asset-folder-dropdown-item.component.ts b/frontend/app/shared/components/assets/asset-folder-dropdown-item.component.ts index 673793156..487060b92 100644 --- a/frontend/app/shared/components/assets/asset-folder-dropdown-item.component.ts +++ b/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) { diff --git a/frontend/app/shared/components/assets/asset-folder-dropdown.component.ts b/frontend/app/shared/components/assets/asset-folder-dropdown.component.ts index dd58ebcf9..3d46f752b 100644 --- a/frontend/app/shared/components/assets/asset-folder-dropdown.component.ts +++ b/frontend/app/shared/components/assets/asset-folder-dropdown.component.ts @@ -59,7 +59,7 @@ export class AssetFolderDropdownComponent extends StatefulControlComponent { let parent = this.root; diff --git a/frontend/app/shared/services/assets.service.spec.ts b/frontend/app/shared/services/assets.service.spec.ts index 2f80e3661..e05ec2f9a 100644 --- a/frontend/app/shared/services/assets.service.spec.ts +++ b/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(); diff --git a/frontend/app/shared/services/assets.service.ts b/frontend/app/shared/services/assets.service.ts index 9e71200dd..835ec6817 100644 --- a/frontend/app/shared/services/assets.service.ts +++ b/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 { - const url = this.apiUrl.buildUrl(`api/apps/${appName}/assets/folders?parentId=${parentId}`); + public getAssetFolders(appName: string, parentId: string, scope: AssetFolderScope): Observable { + 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 }) => {