diff --git a/src/Elastic.Clients.Elasticsearch/Api/IndexManagement/GetAliasResponse.cs b/src/Elastic.Clients.Elasticsearch/Api/IndexManagement/GetAliasResponse.cs new file mode 100644 index 00000000000..d0a3fdcd288 --- /dev/null +++ b/src/Elastic.Clients.Elasticsearch/Api/IndexManagement/GetAliasResponse.cs @@ -0,0 +1,27 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Elastic.Clients.Elasticsearch.IndexManagement; + +public partial class GetAliasResponse +{ + /// + /// A dictionary containing the organised by . + /// + [JsonIgnore] + public IReadOnlyDictionary Aliases => BackingDictionary; + + /// + /// Checks if a response is functionally valid or not. + /// This is a client abstraction to have a single property to check whether there was something wrong with a request. + /// + /// The aliases endpoint returns a 404 when some of the specified alias names for an index cannot be found. For such partial responses, + /// the client considers the response to be valid. + /// + /// + public override bool IsValidResponse => base.IsValidResponse || Aliases.Count > 0; +} diff --git a/src/Elastic.Clients.Elasticsearch/Api/IndexManagement/GetFieldMappingResponse.cs b/src/Elastic.Clients.Elasticsearch/Api/IndexManagement/GetFieldMappingResponse.cs new file mode 100644 index 00000000000..35e2c3b3289 --- /dev/null +++ b/src/Elastic.Clients.Elasticsearch/Api/IndexManagement/GetFieldMappingResponse.cs @@ -0,0 +1,14 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Elastic.Clients.Elasticsearch.IndexManagement; + +public partial class GetFieldMappingResponse +{ + [JsonIgnore] + public IReadOnlyDictionary FieldMappings => BackingDictionary; +} diff --git a/src/Elastic.Clients.Elasticsearch/Api/IndexManagement/GetIndexResponse.cs b/src/Elastic.Clients.Elasticsearch/Api/IndexManagement/GetIndexResponse.cs new file mode 100644 index 00000000000..9b33d1544fd --- /dev/null +++ b/src/Elastic.Clients.Elasticsearch/Api/IndexManagement/GetIndexResponse.cs @@ -0,0 +1,14 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Elastic.Clients.Elasticsearch.IndexManagement; + +public partial class GetIndexResponse +{ + [JsonIgnore] + public IReadOnlyDictionary Indices => BackingDictionary; +} diff --git a/src/Elastic.Clients.Elasticsearch/Api/IndexManagement/GetMappingResponse.cs b/src/Elastic.Clients.Elasticsearch/Api/IndexManagement/GetMappingResponse.cs index a66ef5fd37e..bfe6ac31e30 100644 --- a/src/Elastic.Clients.Elasticsearch/Api/IndexManagement/GetMappingResponse.cs +++ b/src/Elastic.Clients.Elasticsearch/Api/IndexManagement/GetMappingResponse.cs @@ -3,12 +3,14 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Text.Json.Serialization; using Elastic.Clients.Elasticsearch.Mapping; namespace Elastic.Clients.Elasticsearch.IndexManagement; public partial class GetMappingResponse { + [JsonIgnore] public IReadOnlyDictionary Indices => BackingDictionary; } diff --git a/src/Elastic.Clients.Elasticsearch/Api/IndexManagement/GetTemplateResponse.cs b/src/Elastic.Clients.Elasticsearch/Api/IndexManagement/GetTemplateResponse.cs new file mode 100644 index 00000000000..bc09793b8ad --- /dev/null +++ b/src/Elastic.Clients.Elasticsearch/Api/IndexManagement/GetTemplateResponse.cs @@ -0,0 +1,14 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Elastic.Clients.Elasticsearch.IndexManagement; + +public partial class GetTemplateResponse +{ + [JsonIgnore] + public IReadOnlyDictionary TemplateMappings => BackingDictionary; +} diff --git a/src/Elastic.Clients.Elasticsearch/Client/IndicesNamespace.cs b/src/Elastic.Clients.Elasticsearch/Client/IndicesNamespace.cs index 5dc4647e5e1..c665931e12f 100644 --- a/src/Elastic.Clients.Elasticsearch/Client/IndicesNamespace.cs +++ b/src/Elastic.Clients.Elasticsearch/Client/IndicesNamespace.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using System.Threading; +using System; namespace Elastic.Clients.Elasticsearch.IndexManagement; @@ -35,4 +36,20 @@ public Task RefreshAsync(Indices indices, CancellationToken can request.BeforeRequest(); return DoRequestAsync(request, cancellationToken); } + + public virtual GetAliasResponse GetAlias(Indices indicies, Action configureRequest) + { + var descriptor = new GetAliasRequestDescriptor(indicies); + configureRequest?.Invoke(descriptor); + descriptor.BeforeRequest(); + return DoRequest(descriptor); + } + + public virtual Task GetAliasAsync(Indices indicies, Action configureRequest, CancellationToken cancellationToken = default) + { + var descriptor = new GetAliasRequestDescriptor(indicies); + configureRequest?.Invoke(descriptor); + descriptor.BeforeRequest(); + return DoRequestAsync(descriptor, cancellationToken); + } } diff --git a/tests/Tests.Configuration/tests.default.yaml b/tests/Tests.Configuration/tests.default.yaml index d2c61ab164f..c28b1f5cc84 100644 --- a/tests/Tests.Configuration/tests.default.yaml +++ b/tests/Tests.Configuration/tests.default.yaml @@ -8,7 +8,7 @@ mode: u # the elasticsearch version that should be started # Can be a snapshot version of sonatype or "latest" to get the latest snapshot of sonatype -elasticsearch_version: 8.4.3 +elasticsearch_version: 8.5.2 # cluster filter allows you to only run the integration tests of a particular cluster (cluster suffix not needed) # cluster_filter: # whether we want to forcefully reseed on the node, if you are starting the tests with a node already running diff --git a/tests/Tests.Core/ManagedElasticsearch/NodeSeeders/DefaultSeeder.cs b/tests/Tests.Core/ManagedElasticsearch/NodeSeeders/DefaultSeeder.cs index 339d500b9d1..cf0e5e2c364 100644 --- a/tests/Tests.Core/ManagedElasticsearch/NodeSeeders/DefaultSeeder.cs +++ b/tests/Tests.Core/ManagedElasticsearch/NodeSeeders/DefaultSeeder.cs @@ -240,8 +240,8 @@ public async Task CreateIndicesAsync() { aliases = new Dictionary { - { "projects-alias", new { } }, - { "projects-only", new { filter = new { term = new { join = new { value = "project" }}}} } + { ProjectsAliasName, new { } }, + { ProjectsAliasFilter, new { filter = new { term = new { join = new { value = "project" }}}} } }, mappings = new { diff --git a/tests/Tests/IndexManagement/AliasManagement/GetAliasApiTests.cs b/tests/Tests/IndexManagement/AliasManagement/GetAliasApiTests.cs new file mode 100644 index 00000000000..9f4fb768fc2 --- /dev/null +++ b/tests/Tests/IndexManagement/AliasManagement/GetAliasApiTests.cs @@ -0,0 +1,109 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +using System; +using Elastic.Clients.Elasticsearch.IndexManagement; +using Tests.Core.ManagedElasticsearch.Clusters; +using Tests.Core.ManagedElasticsearch.NodeSeeders; +using Tests.Domain; +using Tests.Framework.EndpointTests; +using Tests.Framework.EndpointTests.TestState; + +namespace Tests.IndexManagement.AliasManagement; + +public class GetAliasApiTests : ApiIntegrationTestBase +{ + private static readonly Names Names = Infer.Names(DefaultSeeder.ProjectsAliasName); + + public GetAliasApiTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { } + + protected override bool ExpectIsValid => true; + protected override int ExpectStatusCode => 200; + protected override HttpMethod ExpectHttpMethod => HttpMethod.GET; + protected override bool SupportsDeserialization => false; + protected override string ExpectedUrlPathAndQuery => $"_all/_alias/{DefaultSeeder.ProjectsAliasName}"; + + protected override GetAliasRequest Initializer => new(Indices.All, Names); + protected override Action Fluent => d => d.Name(Names); + + protected override LazyResponses ClientUsage() => Calls( + (client, f) => client.Indices.GetAlias(Indices.All, f), + (client, f) => client.Indices.GetAliasAsync(Indices.All, f), + (client, r) => client.Indices.GetAlias(r), + (client, r) => client.Indices.GetAliasAsync(r) + ); + + protected override void ExpectResponse(GetAliasResponse response) + { + response.Aliases.Should().NotBeEmpty($"expect to find indices pointing to {DefaultSeeder.ProjectsAliasName}"); + var indexAliases = response.Aliases[Infer.Index()]; + indexAliases.Should().NotBeNull("expect to find alias for project"); + indexAliases.Aliases.Should().NotBeEmpty("expect to find aliases dictionary definitions for project"); + var alias = indexAliases.Aliases[DefaultSeeder.ProjectsAliasName]; + alias.Should().NotBeNull(); + } +} + +// TODO: Support exception information from specification and avoid default error response deserialization in transport + +//public class GetAliasPartialMatchApiTests : ApiIntegrationTestBase +//{ +// private static readonly Names Names = Infer.Names(DefaultSeeder.ProjectsAliasName, "x", "y"); + +// public GetAliasPartialMatchApiTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { } + +// protected override bool ExpectIsValid => true; +// protected override int ExpectStatusCode => 404; +// protected override HttpMethod ExpectHttpMethod => HttpMethod.GET; +// protected override bool SupportsDeserialization => false; +// protected override string ExpectedUrlPathAndQuery => $"_all/_alias/{DefaultSeeder.ProjectsAliasName}%2Cx%2Cy"; + +// protected override GetAliasRequest Initializer => new(Indices.All, Names); +// protected override Action Fluent => d => d.Name(Names); + +// protected override LazyResponses ClientUsage() => Calls( +// (client, f) => client.Indices.GetAlias(Indices.All, f), +// (client, f) => client.Indices.GetAliasAsync(Indices.All, f), +// (client, r) => client.Indices.GetAlias(r), +// (client, r) => client.Indices.GetAliasAsync(r) +// ); + +// protected override void ExpectResponse(GetAliasResponse response) +// { +// response.Aliases.Should().NotBeNull(); +// response.Aliases.Count.Should().BeGreaterThan(0); +// } +//} + +public class GetAliasNotFoundApiTests : ApiIntegrationTestBase +{ + private static readonly Names Names = Infer.Names("bad-alias"); + + public GetAliasNotFoundApiTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { } + + protected override bool ExpectIsValid => false; + protected override int ExpectStatusCode => 404; + protected override HttpMethod ExpectHttpMethod => HttpMethod.GET; + protected override bool SupportsDeserialization => false; + protected override string ExpectedUrlPathAndQuery => $"_all/_alias/bad-alias"; + + protected override GetAliasRequest Initializer => new(Indices.All, Names); + protected override Action Fluent => d => d.Name(Names); + + protected override LazyResponses ClientUsage() => Calls( + (client, f) => client.Indices.GetAlias(Indices.All, f), + (client, f) => client.Indices.GetAliasAsync(Indices.All, f), + (client, r) => client.Indices.GetAlias(r), + (client, r) => client.Indices.GetAliasAsync(r) + ); + + protected override void ExpectResponse(GetAliasResponse response) + { + response.ElasticsearchServerError.Should().NotBeNull(); + response.ElasticsearchServerError.Error.Reason.Should().Contain("missing"); + + response.Aliases.Should().NotBeNull(); + response.Aliases.Should().BeEmpty(); + } +} diff --git a/tests/Tests/IndexManagement/MappingManagement/GetFieldMappingApiTests.cs b/tests/Tests/IndexManagement/MappingManagement/GetFieldMappingApiTests.cs new file mode 100644 index 00000000000..dd76afc86b1 --- /dev/null +++ b/tests/Tests/IndexManagement/MappingManagement/GetFieldMappingApiTests.cs @@ -0,0 +1,86 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +using System; +using Elastic.Clients.Elasticsearch.IndexManagement; +using Elastic.Clients.Elasticsearch.Mapping; +using Tests.Core.ManagedElasticsearch.Clusters; +using Tests.Domain; +using Tests.Framework.EndpointTests; +using Tests.Core.Extensions; +using Tests.Framework.EndpointTests.TestState; + +namespace Tests.IndexManagement.MappingManagement; + +public class GetFieldMappingApiTests : ApiIntegrationTestBase, GetFieldMappingRequest> +{ + // TODO: Introduce percolation assertions once seeded + + private static readonly Fields Fields = Infer.Fields(p => p.Name, p => p.LeadDeveloper.IpAddress); + private static readonly Field NameField = Infer.Field(p => p.Name); + private static readonly Indices OnIndices = Infer.Index()/*.And()*/; + + public GetFieldMappingApiTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { } + + protected override bool ExpectIsValid => true; + protected override int ExpectStatusCode => 200; + protected override HttpMethod ExpectHttpMethod => HttpMethod.GET; + protected override GetFieldMappingRequest Initializer => new(OnIndices, Fields); + protected override Action> Fluent => d => d.Indices(OnIndices); + + protected override string ExpectedUrlPathAndQuery => "/project/_mapping/field/name%2CleadDeveloper.ipAddress"; + + protected override LazyResponses ClientUsage() => Calls( + (client, f) => client.Indices.GetFieldMapping(Fields, f), + (client, f) => client.Indices.GetFieldMappingAsync(Fields, f), + (client, r) => client.Indices.GetFieldMapping(r), + (client, r) => client.Indices.GetFieldMappingAsync(r) + ); + + protected override GetFieldMappingRequestDescriptor NewDescriptor() => new(Fields); + + protected override void ExpectResponse(GetFieldMappingResponse response) + { + response.FieldMappings.Should() + .NotBeEmpty("expect indices on the response") + .And.ContainKey(Infer.Index(), "expect project to return field mappings"); + //.And.ContainKey(Infer.Index(), "expect project percolation to return field mappings"); + + var projectMappings = response.FieldMappings[Infer.Index()]; + projectMappings.Should().NotBeNull("project mapping value in the dictionary should not point to a null value"); + + // TODO - Reintroduce this once GetFieldMappingResponse.FieldMappings uses ResolvableReadOnlyDictionaryConverter + //projectMappings.Mappings.Should() + // .NotBeEmpty("project has fields so should contain a type mapping") + // .And.ContainKey(NameField, "project mappings should have 'name'"); + + //var fieldTypeMapping = projectMappings.Mappings[NameField]; + //fieldTypeMapping.Should().NotBeNull("name field mapping should exist"); + //fieldTypeMapping.FullName.Should().NotBeNullOrEmpty(); + //fieldTypeMapping.Mapping.Should() + // .NotBeEmpty("field type mapping should return a `mapping` with the field information") + // .And.HaveCount(1, "field type mappings only return information from a single field") + // .And.ContainKey(NameField); + + //var fieldMapping = fieldTypeMapping.Mapping[NameField]; + //AssertNameFieldMapping(fieldMapping); + + //fieldMapping = response.MappingFor(NameField); + //AssertNameFieldMapping(fieldMapping); + + //fieldMapping = response.MappingFor(p => p.Name); + //AssertNameFieldMapping(fieldMapping); + } + + private static void AssertNameFieldMapping(FieldMapping fieldMapping) + { + fieldMapping.Should().NotBeNull("expected to find name on field type mapping for project"); + + //var nameKeyword = fieldMapping as KeywordProperty; + //nameKeyword.Should().NotBeNull("the field type is a keyword mapping"); + //nameKeyword.Store.Should().BeTrue("name is keyword field that has store enabled"); + //nameKeyword.Fields.Should().NotBeEmpty().And.HaveCount(2); + //nameKeyword.Fields["standard"].Should().NotBeNull(); + } +} diff --git a/tests/Tests/IndexManagement/Mapping/GetMappingApiTests.cs b/tests/Tests/IndexManagement/MappingManagement/GetMappingApiTests.cs similarity index 100% rename from tests/Tests/IndexManagement/Mapping/GetMappingApiTests.cs rename to tests/Tests/IndexManagement/MappingManagement/GetMappingApiTests.cs