Skip to content

Commit e2c8a6a

Browse files
authored
Merge pull request #193 from umbraco/feature/algolia-v14
Feature/algolia v14
2 parents 4cbbf97 + 5baaed3 commit e2c8a6a

File tree

96 files changed

+10366
-1310
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+10366
-1310
lines changed

src/Umbraco.Cms.Integrations.Search.Algolia/AlgoliaComposer.cs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
using Algolia.Search.Models.Search;
2-
32
using Microsoft.Extensions.DependencyInjection;
4-
3+
using Microsoft.OpenApi.Models;
4+
using Swashbuckle.AspNetCore.SwaggerGen;
55
using Umbraco.Cms.Core.Composing;
66
using Umbraco.Cms.Core.DependencyInjection;
77
using Umbraco.Cms.Core.Notifications;
8-
using Umbraco.Cms.Integrations.Search.Algolia;
98
using Umbraco.Cms.Integrations.Search.Algolia.Builders;
109
using Umbraco.Cms.Integrations.Search.Algolia.Configuration;
1110
using Umbraco.Cms.Integrations.Search.Algolia.Extensions;
@@ -14,7 +13,7 @@
1413
using Umbraco.Cms.Integrations.Search.Algolia.Models;
1514
using Umbraco.Cms.Integrations.Search.Algolia.Services;
1615

17-
namespace Umbraco.Cms.Integrations.Crm.ActiveCampaign
16+
namespace Umbraco.Cms.Integrations.Search.Algolia
1817
{
1918
public class AlgoliaComposer : IComposer
2019
{
@@ -23,9 +22,9 @@ public void Compose(IUmbracoBuilder builder)
2322
builder.AddNotificationHandler<UmbracoApplicationStartingNotification, RunAlgoliaIndicesMigration>();
2423

2524
builder.AddNotificationAsyncHandler<ContentCacheRefresherNotification, AlgoliaContentCacheRefresherHandler>();
26-
27-
builder.Services.AddOptions<AlgoliaSettings>()
28-
.Bind(builder.Config.GetSection(Constants.SettingsPath));
25+
26+
builder.Services.AddOptions<AlgoliaSettings>()
27+
.Bind(builder.Config.GetSection(Constants.SettingsPath));
2928

3029
builder.Services.AddSingleton<IAlgoliaIndexService, AlgoliaIndexService>();
3130

@@ -38,6 +37,21 @@ public void Compose(IUmbracoBuilder builder)
3837
builder.Services.AddScoped<IAlgoliaSearchPropertyIndexValueFactory, AlgoliaSearchPropertyIndexValueFactory>();
3938

4039
builder.AddAlgoliaConverters();
40+
41+
// Generate Swagger documentation for Algolia Search API
42+
builder.Services.Configure<SwaggerGenOptions>(options =>
43+
{
44+
options.SwaggerDoc(
45+
Constants.ManagementApi.ApiName,
46+
new OpenApiInfo
47+
{
48+
Title = Constants.ManagementApi.ApiTitle,
49+
Version = "Latest",
50+
Description = $"Describes the {Constants.ManagementApi.ApiTitle} available for handling indices."
51+
});
52+
53+
options.CustomOperationIds(e => $"{e.ActionDescriptor.RouteValues["action"]}");
54+
});
4155
}
4256

4357
}

src/Umbraco.Cms.Integrations.Search.Algolia/AlgoliaDashboard.cs

Lines changed: 0 additions & 17 deletions
This file was deleted.
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
using Asp.Versioning;
2+
using Microsoft.AspNetCore.Http;
3+
using Microsoft.AspNetCore.Mvc;
4+
using Microsoft.Extensions.Logging;
5+
using System.Text.Json;
6+
using Umbraco.Cms.Core.Routing;
7+
using Umbraco.Cms.Core.Services;
8+
using Umbraco.Cms.Core.Web;
9+
using Umbraco.Cms.Integrations.Search.Algolia.Builders;
10+
using Umbraco.Cms.Integrations.Search.Algolia.Migrations;
11+
using Umbraco.Cms.Integrations.Search.Algolia.Models;
12+
using Umbraco.Cms.Integrations.Search.Algolia.Models.ContentTypeDtos;
13+
using Umbraco.Cms.Integrations.Search.Algolia.Services;
14+
15+
namespace Umbraco.Cms.Integrations.Search.Algolia.Api.Management.Controllers;
16+
17+
[ApiVersion("1.0")]
18+
[ApiExplorerSettings(GroupName = Constants.ManagementApi.GroupName)]
19+
public class BuildIndexController : SearchControllerBase
20+
{
21+
private readonly IAlgoliaIndexDefinitionStorage<AlgoliaIndex> _indexStorage;
22+
private readonly IAlgoliaIndexService _indexService;
23+
private readonly IUmbracoContextFactory _umbracoContextFactory;
24+
private readonly IContentService _contentService;
25+
private readonly ILogger<BuildIndexController> _logger;
26+
private readonly IUserService _userService;
27+
private readonly IPublishedUrlProvider _urlProvider;
28+
private readonly IAlgoliaSearchPropertyIndexValueFactory _algoliaSearchPropertyIndexValueFactory;
29+
private readonly IRecordBuilderFactory _recordBuilderFactory;
30+
31+
public BuildIndexController(
32+
IAlgoliaIndexDefinitionStorage<AlgoliaIndex> indexStorage,
33+
IUmbracoContextFactory umbracoContextFactory,
34+
IContentService contentService,
35+
ILogger<BuildIndexController> logger,
36+
IAlgoliaIndexService indexService,
37+
IUserService userService,
38+
IPublishedUrlProvider urlProvider,
39+
IAlgoliaSearchPropertyIndexValueFactory algoliaSearchPropertyIndexValueFactory,
40+
IRecordBuilderFactory recordBuilderFactory)
41+
{
42+
_indexStorage = indexStorage;
43+
_umbracoContextFactory = umbracoContextFactory;
44+
_contentService = contentService;
45+
_logger = logger;
46+
_indexService = indexService;
47+
_userService = userService;
48+
_urlProvider = urlProvider;
49+
_algoliaSearchPropertyIndexValueFactory = algoliaSearchPropertyIndexValueFactory;
50+
_recordBuilderFactory = recordBuilderFactory;
51+
}
52+
53+
[HttpPost("index/build")]
54+
[ProducesResponseType(typeof(Result), StatusCodes.Status200OK)]
55+
public async Task<IActionResult> BuildIndex([FromBody] IndexConfiguration indexConfiguration)
56+
{
57+
AlgoliaIndex index = _indexStorage.GetById(indexConfiguration.Id);
58+
List<Record> payload = [];
59+
60+
IEnumerable<ContentTypeDto>? indexContentData = JsonSerializer.Deserialize<IEnumerable<ContentTypeDto>>(index.SerializedData);
61+
if (indexContentData is null)
62+
{
63+
// TODO => handle null result
64+
return BadRequest();
65+
}
66+
67+
foreach (var contentDataItem in indexContentData)
68+
{
69+
using var ctx = _umbracoContextFactory.EnsureUmbracoContext();
70+
var contentType = ctx.UmbracoContext.Content?.GetContentType(contentDataItem.Alias);
71+
72+
if (contentType is null)
73+
{
74+
// TODO => handle null content type
75+
continue;
76+
}
77+
78+
// use GetPagedOfTypes as filter is nullable here, but not on GetPagedOfType
79+
var contentItems = _contentService.GetPagedOfTypes([contentType.Id], 0, int.MaxValue, out _, null);
80+
81+
_logger.LogInformation("Building index for {ContentType} with {Count} items", contentDataItem.Alias, contentItems.Count());
82+
83+
foreach (var contentItem in contentItems.Where(p => !p.Trashed))
84+
{
85+
var record = new ContentRecordBuilder(_userService, _urlProvider, _algoliaSearchPropertyIndexValueFactory, _recordBuilderFactory, _umbracoContextFactory)
86+
.BuildFromContent(contentItem, (p) => contentDataItem.Properties.Any(q => q.Alias == p.Alias))
87+
.Build();
88+
89+
payload.Add(record);
90+
}
91+
}
92+
93+
var result = await _indexService.PushData(index.Name, payload);
94+
95+
return Ok(result);
96+
}
97+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using Asp.Versioning;
2+
using Microsoft.AspNetCore.Http;
3+
using Microsoft.AspNetCore.Mvc;
4+
using Umbraco.Cms.Integrations.Search.Algolia.Migrations;
5+
using Umbraco.Cms.Integrations.Search.Algolia.Models;
6+
using Umbraco.Cms.Integrations.Search.Algolia.Services;
7+
8+
namespace Umbraco.Cms.Integrations.Search.Algolia.Api.Management.Controllers;
9+
10+
[ApiVersion("1.0")]
11+
[ApiExplorerSettings(GroupName = Constants.ManagementApi.GroupName)]
12+
public class DeleteIndexController : SearchControllerBase
13+
{
14+
private readonly IAlgoliaIndexService _indexService;
15+
private readonly IAlgoliaIndexDefinitionStorage<AlgoliaIndex> _indexStorage;
16+
17+
public DeleteIndexController(
18+
IAlgoliaIndexService indexService,
19+
IAlgoliaIndexDefinitionStorage<AlgoliaIndex> indexStorage)
20+
{
21+
_indexService = indexService;
22+
_indexStorage = indexStorage;
23+
}
24+
25+
[HttpDelete("index/{id:int}")]
26+
[ProducesResponseType(typeof(Result), StatusCodes.Status200OK)]
27+
public async Task<IActionResult> DeleteIndex(int id)
28+
{
29+
var indexName = _indexStorage.GetById(id).Name;
30+
31+
_indexStorage.Delete(id);
32+
33+
await _indexService.DeleteIndex(indexName);
34+
35+
return Ok(Result.Ok());
36+
}
37+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
using Asp.Versioning;
2+
using Microsoft.AspNetCore.Http;
3+
using Microsoft.AspNetCore.Mvc;
4+
using System.Text.Json;
5+
using Umbraco.Cms.Core.Services;
6+
7+
using Umbraco.Cms.Integrations.Search.Algolia.Migrations;
8+
using Umbraco.Cms.Integrations.Search.Algolia.Models;
9+
using Umbraco.Cms.Integrations.Search.Algolia.Models.ContentTypeDtos;
10+
using Umbraco.Cms.Integrations.Search.Algolia.Services;
11+
12+
namespace Umbraco.Cms.Integrations.Search.Algolia.Api.Management.Controllers;
13+
14+
[ApiVersion("1.0")]
15+
[ApiExplorerSettings(GroupName = Constants.ManagementApi.GroupName)]
16+
public class GetContentTypesController : SearchControllerBase
17+
{
18+
private readonly IContentTypeService _contentTypeService;
19+
private readonly IAlgoliaIndexDefinitionStorage<AlgoliaIndex> _indexStorage;
20+
21+
public GetContentTypesController(
22+
IAlgoliaIndexDefinitionStorage<AlgoliaIndex> indexStorage,
23+
IContentTypeService contentTypeService)
24+
{
25+
_indexStorage = indexStorage;
26+
_contentTypeService = contentTypeService;
27+
}
28+
29+
[HttpGet("content-type")]
30+
[ProducesResponseType(typeof(List<ContentTypeDto>), StatusCodes.Status200OK)]
31+
public IActionResult Get() => Ok(GetContentTypes());
32+
33+
[HttpGet("content-type/index/{id:int}")]
34+
[ProducesResponseType(typeof(List<ContentTypeDto>), StatusCodes.Status200OK)]
35+
public IActionResult GetByIndexId(int id) => Ok(GetContentTypes(id));
36+
37+
private List<ContentTypeDto> GetContentTypes(int? id = null)
38+
{
39+
IndexConfiguration indexConfiguration = new();
40+
List<ContentTypeDto> list = [];
41+
42+
if (id is not null)
43+
{
44+
var index = _indexStorage.GetById(id.Value);
45+
46+
if (index is not null)
47+
{
48+
indexConfiguration.Id = index.Id;
49+
indexConfiguration.Name = index.Name;
50+
indexConfiguration.ContentData = JsonSerializer.Deserialize<IEnumerable<ContentTypeDto>>(index.SerializedData) ?? [];
51+
}
52+
}
53+
54+
var contentTypes = _contentTypeService.GetAll();
55+
foreach (var contentType in contentTypes)
56+
{
57+
var properties = new List<ContentTypePropertyDto>();
58+
foreach (var propertyGroup in contentType.PropertyGroups)
59+
{
60+
if (propertyGroup.PropertyTypes is null)
61+
{
62+
continue;
63+
}
64+
65+
foreach (var property in propertyGroup.PropertyTypes)
66+
{
67+
var contentTypePropertyDto = new ContentTypePropertyDto
68+
{
69+
Id = property.Id,
70+
Alias = property.Alias,
71+
Icon = "icon-document",
72+
Name = property.Name,
73+
Group = propertyGroup.Name ?? string.Empty,
74+
};
75+
if (indexConfiguration != null && indexConfiguration.ContentData != null)
76+
{
77+
contentTypePropertyDto.Selected = indexConfiguration.ContentData
78+
.Any(p => p.Alias == contentType.Alias && p.Properties.Any(q => q.Alias == property.Alias));
79+
}
80+
81+
properties.Add(contentTypePropertyDto);
82+
}
83+
}
84+
85+
list.Add(new ContentTypeDto
86+
{
87+
Id = contentType.Id,
88+
Icon = contentType.Icon ?? string.Empty,
89+
Alias = contentType.Alias,
90+
Name = contentType.Name ?? string.Empty,
91+
Selected = indexConfiguration != null
92+
&& indexConfiguration.ContentData != null
93+
&& indexConfiguration.ContentData.Any(p => p.Alias == contentType.Alias),
94+
Properties = properties.AsEnumerable()
95+
});
96+
}
97+
98+
return list;
99+
}
100+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using Asp.Versioning;
2+
using Microsoft.AspNetCore.Http;
3+
using Microsoft.AspNetCore.Mvc;
4+
using System.Text.Json;
5+
using Umbraco.Cms.Integrations.Search.Algolia.Migrations;
6+
using Umbraco.Cms.Integrations.Search.Algolia.Models;
7+
using Umbraco.Cms.Integrations.Search.Algolia.Models.ContentTypeDtos;
8+
using Umbraco.Cms.Integrations.Search.Algolia.Services;
9+
10+
namespace Umbraco.Cms.Integrations.Search.Algolia.Api.Management.Controllers;
11+
12+
[ApiVersion("1.0")]
13+
[ApiExplorerSettings(GroupName = Constants.ManagementApi.GroupName)]
14+
public class GetIndexByIdController : SearchControllerBase
15+
{
16+
private readonly IAlgoliaIndexDefinitionStorage<AlgoliaIndex> _indexStorage;
17+
18+
public GetIndexByIdController(IAlgoliaIndexDefinitionStorage<AlgoliaIndex> indexStorage) => _indexStorage = indexStorage;
19+
20+
[HttpGet("index/{id:int}")]
21+
[ProducesResponseType(typeof(IndexConfiguration), StatusCodes.Status200OK)]
22+
public IActionResult GetIndexById(int id)
23+
{
24+
var index = _indexStorage.GetById(id);
25+
26+
var indexConfiguration = new IndexConfiguration
27+
{
28+
Id = index.Id,
29+
Name = index.Name,
30+
ContentData = JsonSerializer.Deserialize<IEnumerable<ContentTypeDto>>(index.SerializedData) ?? []
31+
};
32+
33+
return Ok(indexConfiguration);
34+
}
35+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using Asp.Versioning;
2+
using Microsoft.AspNetCore.Http;
3+
using Microsoft.AspNetCore.Mvc;
4+
using System.Text.Json;
5+
using Umbraco.Cms.Integrations.Search.Algolia.Migrations;
6+
using Umbraco.Cms.Integrations.Search.Algolia.Models;
7+
using Umbraco.Cms.Integrations.Search.Algolia.Models.ContentTypeDtos;
8+
using Umbraco.Cms.Integrations.Search.Algolia.Services;
9+
10+
namespace Umbraco.Cms.Integrations.Search.Algolia.Api.Management.Controllers;
11+
12+
[ApiVersion("1.0")]
13+
[ApiExplorerSettings(GroupName = Constants.ManagementApi.GroupName)]
14+
public class GetIndicesController : SearchControllerBase
15+
{
16+
private readonly IAlgoliaIndexDefinitionStorage<AlgoliaIndex> _indexStorage;
17+
18+
public GetIndicesController(
19+
IAlgoliaIndexDefinitionStorage<AlgoliaIndex> indexStorage) => _indexStorage = indexStorage;
20+
21+
[HttpGet("index")]
22+
[ProducesResponseType(typeof(IEnumerable<IndexConfiguration>), StatusCodes.Status200OK)]
23+
public IActionResult GetIndices()
24+
{
25+
var indices = _indexStorage.Get().Select(p => new IndexConfiguration
26+
{
27+
Id = p.Id,
28+
Name = p.Name,
29+
ContentData = JsonSerializer.Deserialize<IEnumerable<ContentTypeDto>>(p.SerializedData) ?? []
30+
});
31+
32+
return Ok(indices);
33+
}
34+
}

0 commit comments

Comments
 (0)