Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ jobs:
strategy:
fail-fast: false
matrix:
version: ["1.32.27", "1.33.18", "1.34.20", "1.35.16", "1.36.10", "1.37.5-e0fe0d5.amd64"]
version: ["1.32.27", "1.33.18", "1.34.20", "1.35.23", "1.36.21", "1.37.12", "1.38.4"]
uses: ./.github/workflows/test-on-weaviate-version.yml
secrets: inherit
with:
Expand Down
29 changes: 27 additions & 2 deletions src/Weaviate.Client.Tests/Integration/TestCollections.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@ public async Task Test_Collections_Export_Cases(string key)

var expected = CollectionConfig.FromCollectionCreate(c);

if (ServerVersionIsInRange("1.38.0") && expected.ReplicationConfig is not null)
{
// Weaviate 1.38 removed the per-collection asyncEnabled setting: the server
// derives it as `factor > 1` and ignores the value sent at creation.
expected.ReplicationConfig.AsyncEnabled = expected.ReplicationConfig.Factor > 1;
}

Assert.Equal(expected, export);
}
#endif
Expand Down Expand Up @@ -404,7 +411,16 @@ public async Task Test_Collections_Export_NonDefaultValues_Sharding()

// ReplicationConfig validation
Assert.NotNull(export.ReplicationConfig);
Assert.True(export.ReplicationConfig.AsyncEnabled);
if (ServerVersionIsInRange("1.38.0"))
{
// Weaviate 1.38 removed the per-collection asyncEnabled setting: the server
// derives it as `factor > 1` (factor is 1 here) and ignores the value sent.
Assert.False(export.ReplicationConfig.AsyncEnabled);
}
else
{
Assert.True(export.ReplicationConfig.AsyncEnabled);
}
Assert.True(
new[]
{
Expand Down Expand Up @@ -561,7 +577,16 @@ public async Task Test_Collections_Export_NonDefaultValues_MultiTenacy()

// ReplicationConfig validation
Assert.NotNull(export.ReplicationConfig);
Assert.True(export.ReplicationConfig.AsyncEnabled);
if (ServerVersionIsInRange("1.38.0"))
{
// Weaviate 1.38 removed the per-collection asyncEnabled setting: the server
// derives it as `factor > 1` (factor is 1 here) and ignores the value sent.
Assert.False(export.ReplicationConfig.AsyncEnabled);
}
else
{
Assert.True(export.ReplicationConfig.AsyncEnabled);
}
Assert.True(
new[]
{
Expand Down
40 changes: 40 additions & 0 deletions src/Weaviate.Client.Tests/Integration/TestRbacRoles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -574,4 +574,44 @@ public async Task CreateRoleWithMcpPermission()
await _weaviate.Roles.Delete(roleName, TestContext.Current.CancellationToken);
}
}

/// <summary>
/// Tests that create role with namespaces permission round trips (Weaviate 1.38+)
/// </summary>
[Fact]
public async Task CreateRoleWithNamespacesPermission()
{
RequireVersion("1.38.0");
var roleName = MakeRoleName("namespaces");
try
{
await _weaviate.Roles.Delete(roleName, TestContext.Current.CancellationToken);
var created = await _weaviate.Roles.Create(
roleName,
[new Permissions.Namespaces("*") { Manage = true }],
TestContext.Current.CancellationToken
);
Assert.NotNull(created);
Assert.Equal(roleName, created.Name);
Assert.Single(created.Permissions);
var scope = Assert.IsType<Permissions.Namespaces>(created.Permissions.Single());
Assert.True(scope.Manage);
Assert.Equal("*", scope.Resource.Namespace);

var fetched = await _weaviate.Roles.Get(
roleName,
TestContext.Current.CancellationToken
);
Assert.NotNull(fetched);
Assert.Equal(roleName, fetched!.Name);
Assert.Single(fetched.Permissions);
var fetchedScope = Assert.IsType<Permissions.Namespaces>(fetched.Permissions.Single());
Assert.True(fetchedScope.Manage);
Assert.Equal("*", fetchedScope.Resource.Namespace);
}
finally
{
await _weaviate.Roles.Delete(roleName, TestContext.Current.CancellationToken);
}
}
}
49 changes: 49 additions & 0 deletions src/Weaviate.Client.Tests/Unit/PermissionsScopeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,54 @@ public void Backups_Aggregates_ManageBackupsOnly()
Assert.True(backups[0].Manage);
}

/// <summary>
/// Tests that namespaces aggregates manage namespaces only (Weaviate 1.38+)
/// </summary>
[Fact]
public void Namespaces_Aggregates_ManageNamespacesOnly()
{
var resource = new Rest.Dto.Namespaces { Namespace = "ns-.*" };
var permissions = new List<Rest.Dto.Permission>
{
new()
{
Action = Weaviate.Client.Rest.Dto.PermissionAction.Manage_namespaces,
Namespaces = resource,
},
};
var namespaces = Permissions
.Namespaces.Parse(permissions)
.Cast<Permissions.Namespaces>()
.ToList();
Assert.Single(namespaces);
Assert.True(namespaces[0].Manage);
Assert.Equal("ns-.*", namespaces[0].Resource.Namespace);
}

/// <summary>
/// Tests that namespaces round trips through the dto (Weaviate 1.38+)
/// </summary>
[Fact]
public void Namespaces_ToDto_RoundTrips()
{
var scope = new Permissions.Namespaces("team-a") { Manage = true };

var dtos = scope.ToDto().ToList();

var dto = Assert.Single(dtos);
Assert.Equal(Weaviate.Client.Rest.Dto.PermissionAction.Manage_namespaces, dto.Action);
Assert.NotNull(dto.Namespaces);
Assert.Equal("team-a", dto.Namespaces!.Namespace);

var parsed = Assert.Single(Permissions.Namespaces.Parse(dtos));
var roundTripped = Assert.IsType<Permissions.Namespaces>(parsed);
Assert.True(roundTripped.Manage);
Assert.Equal("team-a", roundTripped.Resource.Namespace);

// No actions set -> no permission entries are emitted.
Assert.Empty(new Permissions.Namespaces("team-a").ToDto());
}

/// <summary>
/// Tests that all permission actions are mentioned
/// </summary>
Expand All @@ -411,6 +459,7 @@ public void AllPermissionActions_AreMentioned()
var testedActions = new HashSet<string>
{
"Manage_backups",
"Manage_namespaces",
"Read_cluster",
"Create_data",
"Read_data",
Expand Down
38 changes: 38 additions & 0 deletions src/Weaviate.Client/Models/PermissionResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ public record ReplicateResource(string? Collection = "*", string? Shard = "*");
/// <param name="Alias">Optional alias name (defaults to "*" for all aliases)</param>
public record AliasesResource(string? Collection = "*", string? Alias = "*");

/// <summary>
/// Represents a namespaces resource, optionally scoped to a namespace. Requires Weaviate 1.38 or later.
/// </summary>
/// <param name="Namespace">Optional namespace name or regex pattern (defaults to "*" for all namespaces)</param>
public record NamespacesResource(string? Namespace = "*");

/// <summary>
/// The permission resource extensions class
/// </summary>
Expand Down Expand Up @@ -191,6 +197,16 @@ internal static Rest.Dto.Aliases ToDto(this AliasesResource resource)
return new Rest.Dto.Aliases { Collection = resource.Collection, Alias = resource.Alias };
}

/// <summary>
/// Returns the dto using the specified resource
/// </summary>
/// <param name="resource">The resource</param>
/// <returns>The rest dto namespaces</returns>
internal static Rest.Dto.Namespaces ToDto(this NamespacesResource resource)
{
return new Rest.Dto.Namespaces { Namespace = resource.Namespace };
}

/// <summary>
/// Returns the model using the specified resource
/// </summary>
Expand Down Expand Up @@ -421,4 +437,26 @@ internal static PermissionScope ToModel(
Delete = actions.Contains(Rest.Dto.PermissionAction.Delete_collections),
};
}

/// <summary>
/// Returns the model using the specified resource
/// </summary>
/// <param name="resource">The resource</param>
/// <param name="permissions">The permissions</param>
/// <returns>The permission scope</returns>
internal static PermissionScope ToModel(
this Rest.Dto.Namespaces resource,
IEnumerable<Rest.Dto.Permission> permissions
)
{
var actions = permissions
.Where(p => p.Namespaces == resource)
.Select(p => p.Action)
.ToHashSet();

return new Permissions.Namespaces(resource.Namespace)
{
Manage = actions.Contains(Rest.Dto.PermissionAction.Manage_namespaces),
};
}
}
69 changes: 69 additions & 0 deletions src/Weaviate.Client/Models/RbacPermissions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,74 @@ internal static List<PermissionScope> Parse(IEnumerable<Rest.Dto.Permission> inf
}
}

/// <summary>
/// The namespaces class. Requires Weaviate 1.38 or later.
/// </summary>
/// <seealso cref="PermissionScope"/>
public class Namespaces : PermissionScope
{
/// <summary>
/// Gets the value of the resource
/// </summary>
public NamespacesResource Resource { get; }

/// <summary>
/// Gets or sets the value of the manage
/// </summary>
public bool Manage { get; set; }

/// <summary>
/// Initializes a new instance of the <see cref="Namespaces"/> class
/// </summary>
/// <param name="namespace">The namespace name or regex pattern</param>
public Namespaces(string? @namespace)
: this(new NamespacesResource(@namespace)) { }

/// <summary>
/// Initializes a new instance of the <see cref="Namespaces"/> class
/// </summary>
/// <param name="resource">The resource</param>
/// <exception cref="ArgumentNullException"></exception>
Namespaces(NamespacesResource resource)
{
Resource = resource ?? throw new ArgumentNullException(nameof(resource));
}

/// <summary>
/// Returns the dto
/// </summary>
/// <returns>An enumerable of rest dto permission</returns>
internal override IEnumerable<Rest.Dto.Permission> ToDto()
{
var permissions = new[]
{
(Action: Rest.Dto.PermissionAction.Manage_namespaces, Allowed: Manage),
};

return permissions
.Where(p => p.Allowed)
.Select(p => new Rest.Dto.Permission
{
Namespaces = Resource.ToDto(),
Action = p.Action,
});
}

/// <summary>
/// Parses the infos
/// </summary>
/// <param name="infos">The infos</param>
/// <returns>A list of permission scope</returns>
internal static List<PermissionScope> Parse(IEnumerable<Rest.Dto.Permission> infos)
{
return infos
.Where(i => i.Namespaces != null)
.GroupBy(i => i.Namespaces!)
.Select(group => group.Key.ToModel(group.AsEnumerable()))
.ToList();
}
}

/// <summary>
/// The mcp class
/// </summary>
Expand Down Expand Up @@ -988,6 +1056,7 @@ internal static List<PermissionScope> Parse(IEnumerable<Rest.Dto.Permission> inf
scopes.AddRange(Alias.Parse(infos));
scopes.AddRange(Data.Parse(infos));
scopes.AddRange(Backups.Parse(infos));
scopes.AddRange(Namespaces.Parse(infos));
scopes.AddRange(Mcp.Parse(infos));
scopes.AddRange(Cluster.Parse(infos));
scopes.AddRange(Nodes.Parse(infos));
Expand Down
7 changes: 7 additions & 0 deletions src/Weaviate.Client/Models/Replication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ public enum ReplicationOperationState
/// </summary>
[System.Text.Json.Serialization.JsonStringEnumMemberName("CANCELLED")]
Cancelled,

/// <summary>
/// Replica has finished copying and is being integrated as a queryable member of the
/// shard. Occurs between <see cref="Finalizing"/> and <see cref="Dehydrating"/>.
/// </summary>
[System.Text.Json.Serialization.JsonStringEnumMemberName("INTEGRATING")]
Integrating,
}

/// <summary>
Expand Down
21 changes: 21 additions & 0 deletions src/Weaviate.Client/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#nullable enable
Weaviate.Client.Models.ReplicationOperationState.Integrating = 6 -> Weaviate.Client.Models.ReplicationOperationState
*REMOVED*Weaviate.Client.VectorizerFactory.Text2VecAWSBedrock(string! region, string! model, bool? vectorizeCollectionName = null) -> Weaviate.Client.Models.VectorizerConfig!
*REMOVED*Weaviate.Client.VectorizerFactory.Text2VecAWSSagemaker(string! region, string! endpoint, string? targetModel = null, string? targetVariant = null, bool? vectorizeCollectionName = null) -> Weaviate.Client.Models.VectorizerConfig!
*REMOVED*Weaviate.Client.VectorizerFactory.Text2VecGoogleVertex(string? apiEndpoint = null, string? model = null, string? projectId = null, string? titleProperty = null, int? dimensions = null, string? taskType = null, bool? vectorizeCollectionName = null) -> Weaviate.Client.Models.VectorizerConfig!
Expand All @@ -9,3 +10,23 @@ Weaviate.Client.Models.Vectorizer.Text2VecGoogle.Location.set -> void
Weaviate.Client.VectorizerFactory.Text2VecAWSBedrock(string! region, string! model, int? dimensions = null, bool? vectorizeCollectionName = null) -> Weaviate.Client.Models.VectorizerConfig!
Weaviate.Client.VectorizerFactory.Text2VecAWSSagemaker(string! region, string! endpoint, string? targetModel = null, string? targetVariant = null, int? dimensions = null, bool? vectorizeCollectionName = null) -> Weaviate.Client.Models.VectorizerConfig!
Weaviate.Client.VectorizerFactory.Text2VecGoogleVertex(string? apiEndpoint = null, string? model = null, string? projectId = null, string? titleProperty = null, int? dimensions = null, string? taskType = null, bool? vectorizeCollectionName = null, string? location = null) -> Weaviate.Client.Models.VectorizerConfig!
override Weaviate.Client.Models.NamespacesResource.Equals(object? obj) -> bool
override Weaviate.Client.Models.NamespacesResource.GetHashCode() -> int
override Weaviate.Client.Models.NamespacesResource.ToString() -> string!
static Weaviate.Client.Models.NamespacesResource.operator !=(Weaviate.Client.Models.NamespacesResource? left, Weaviate.Client.Models.NamespacesResource? right) -> bool
static Weaviate.Client.Models.NamespacesResource.operator ==(Weaviate.Client.Models.NamespacesResource? left, Weaviate.Client.Models.NamespacesResource? right) -> bool
virtual Weaviate.Client.Models.NamespacesResource.<Clone>$() -> Weaviate.Client.Models.NamespacesResource!
virtual Weaviate.Client.Models.NamespacesResource.EqualityContract.get -> System.Type!
virtual Weaviate.Client.Models.NamespacesResource.Equals(Weaviate.Client.Models.NamespacesResource? other) -> bool
virtual Weaviate.Client.Models.NamespacesResource.PrintMembers(System.Text.StringBuilder! builder) -> bool
Weaviate.Client.Models.NamespacesResource
Weaviate.Client.Models.NamespacesResource.Deconstruct(out string? Namespace) -> void
Weaviate.Client.Models.NamespacesResource.Namespace.get -> string?
Weaviate.Client.Models.NamespacesResource.Namespace.init -> void
Weaviate.Client.Models.NamespacesResource.NamespacesResource(string? Namespace = "*") -> void
Weaviate.Client.Models.NamespacesResource.NamespacesResource(Weaviate.Client.Models.NamespacesResource! original) -> void
Weaviate.Client.Models.Permissions.Namespaces
Weaviate.Client.Models.Permissions.Namespaces.Manage.get -> bool
Weaviate.Client.Models.Permissions.Namespaces.Manage.set -> void
Weaviate.Client.Models.Permissions.Namespaces.Namespaces(string? namespace) -> void
Weaviate.Client.Models.Permissions.Namespaces.Resource.get -> Weaviate.Client.Models.NamespacesResource!
2 changes: 2 additions & 0 deletions src/Weaviate.Client/ReplicationsClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ private static ReplicationOperationState ParseState(
ReplicationOperationState.Hydrating,
Rest.Dto.ReplicationReplicateDetailsReplicaStatusState.FINALIZING =>
ReplicationOperationState.Finalizing,
Rest.Dto.ReplicationReplicateDetailsReplicaStatusState.INTEGRATING =>
ReplicationOperationState.Integrating,
Rest.Dto.ReplicationReplicateDetailsReplicaStatusState.DEHYDRATING =>
ReplicationOperationState.Dehydrating,
Rest.Dto.ReplicationReplicateDetailsReplicaStatusState.READY =>
Expand Down
Loading
Loading