diff --git a/src/alias/index.ts b/src/alias/index.ts new file mode 100644 index 00000000..2ec31f21 --- /dev/null +++ b/src/alias/index.ts @@ -0,0 +1,79 @@ +import { ConnectionREST } from '../index.js'; +import { WeaviateAlias, WeaviateAliasResponse } from '../openapi/types.js'; +import { Alias, AliasListAllOptions, CreateAliasArgs, UpdateAliasArgs } from './types.js'; + +export interface Aliases { + /** + * Create alias for a collection. + * + * The collection must exist prior to aliasing it. + * One alias cannot be created for multiple collections simultaneously. + * + * @param {string} args.collection Original collection name. + * @param {string} args.alias Alias for collection. + * @returns {Promise} Awaitable promise. + * */ + create: (args: CreateAliasArgs) => Promise; + + /** + * List all aliases defined in the schema. + * + * @param {string | undefined} [opts.collection] Get all aliases defined for this collection. + * @returns {Promise} An array of aliases. + */ + listAll: (opts?: AliasListAllOptions) => Promise; + + /** + * Get information about an alias. + * + * @param {string} alias Alias to fetch. + * @return {Promise} Alias definition. + */ + get: (alias: string) => Promise; + + /** + * Replace target collection the alias points to. + * + * To change the alias that points to the collection, + * delete the alias and create a new one. + * + * @param {string} args.alias Alias to update. + * @param {string} args.collection New collection the alias should point to. + * @return {Promise} Awaitable promise. + */ + update: (args: UpdateAliasArgs) => Promise; + + /** + * Delete a collection alias. + * + * @param {string} alias Alias definition to delete. + * @return {Promise} Awaitable promise. + */ + delete: (alias: string) => Promise; +} + +const alias = (connection: ConnectionREST): Aliases => { + return { + create: (args: CreateAliasArgs) => + connection.postReturn(`/aliases/`, { ...args, class: args.collection }), + listAll: (opts?: AliasListAllOptions) => + connection + .get( + `/aliases${opts?.collection !== undefined ? '/?class=' + opts.collection : ''}` + ) + .then((aliases) => + aliases.aliases !== undefined + ? aliases.aliases.map((alias) => ({ alias: alias.alias, collection: alias.class })) + : [] + ), + get: (alias: string) => + connection + .get(`/aliases/${alias}`) + .then((alias) => ({ alias: alias.alias!, collection: alias.class! })), + update: (args: UpdateAliasArgs) => + connection.put(`/aliases/${args.alias}`, { class: args.newTargetCollection }), + delete: (alias: string) => connection.delete(`/aliases/${alias}`, null), + }; +}; + +export default alias; diff --git a/src/alias/journey.test.ts b/src/alias/journey.test.ts new file mode 100644 index 00000000..b72835d1 --- /dev/null +++ b/src/alias/journey.test.ts @@ -0,0 +1,53 @@ +import weaviate, { WeaviateClient } from '..'; +import { requireAtLeast } from '../../test/version'; +import { Alias } from './types'; + +requireAtLeast(1, 32, 0).describe('manages collection aliases', () => { + let client: WeaviateClient; + const collectionsWithAliases = ['PaulHewson', 'GeorgeBarnes', 'ColsonBaker']; + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + await Promise.all(collectionsWithAliases.map(client.collections.delete)); + await Promise.all(collectionsWithAliases.map((name) => client.collections.create({ name }))); + }); + + it('should create alias', () => { + return Promise.all([ + client.alias.create({ collection: 'PaulHewson', alias: 'Bono' }), + client.alias.create({ collection: 'GeorgeBarnes', alias: 'MachineGunKelly' }), + ]) + .then(() => client.alias.listAll()) + .then((aliases) => { + expect(aliases).not.toBeUndefined(); + expect(aliases).toHaveLength(2); + expect(aliases).toEqual([ + { collection: 'PaulHewson', alias: 'Bono' }, + { collection: 'GeorgeBarnes', alias: 'MachineGunKelly' }, + ]); + }); + }); + + it('should update alias', () => { + return client.alias + .update({ alias: 'MachineGunKelly', newTargetCollection: 'ColsonBaker' }) + .then(() => client.alias.get('MachineGunKelly')) + .then((alias) => { + expect(alias.collection).toEqual('ColsonBaker'); + }); + }); + + it('should delete alias Bono', () => { + return client.alias + .delete('Bono') + .then(() => client.alias.listAll({ collection: 'PaulHewson' })) + .then((aliases) => expect(aliases).toEqual([])); + }); + + it('should delete alias MachineGunKelly', () => { + return client.alias + .delete('MachineGunKelly') + .then(() => client.alias.listAll({ collection: 'ColsonBaker' })) + .then((aliases) => expect(aliases).toEqual([])); + }); +}); diff --git a/src/alias/types.ts b/src/alias/types.ts new file mode 100644 index 00000000..a8262f72 --- /dev/null +++ b/src/alias/types.ts @@ -0,0 +1,18 @@ +export type Alias = { + collection: string; + alias: string; +}; + +export type CreateAliasArgs = { + collection: string; + alias: string; +}; + +export type UpdateAliasArgs = { + newTargetCollection: string; + alias: string; +}; + +export type AliasListAllOptions = { + collection?: string | undefined; +}; diff --git a/src/index.ts b/src/index.ts index 43bb1ac5..9de11976 100644 --- a/src/index.ts +++ b/src/index.ts @@ -38,6 +38,7 @@ import { LiveChecker, OpenidConfigurationGetter, ReadyChecker } from './misc/ind import weaviateV2 from './v2/index.js'; +import alias, { Aliases } from './alias/index.js'; import filter from './collections/filters/index.js'; import { ConsistencyLevel } from './data/replication.js'; import users, { Users } from './users/index.js'; @@ -102,6 +103,7 @@ export type ClientParams = { }; export interface WeaviateClient { + alias: Aliases; backup: Backup; cluster: Cluster; collections: Collections; @@ -224,6 +226,7 @@ async function client(params: ClientParams): Promise { }); const ifc: WeaviateClient = { + alias: alias(connection), backup: backup(connection), cluster: cluster(connection), collections: collections(connection, dbVersionSupport), diff --git a/src/openapi/schema.ts b/src/openapi/schema.ts index 56a18d78..9908f40d 100644 --- a/src/openapi/schema.ts +++ b/src/openapi/schema.ts @@ -41,11 +41,31 @@ export interface paths { }; }; '/replication/replicate': { + /** Begins an asynchronous operation to move or copy a specific shard replica from its current node to a designated target node. The operation involves copying data, synchronizing, and potentially decommissioning the source replica. */ post: operations['replicate']; + delete: operations['deleteAllReplications']; + }; + '/replication/replicate/force-delete': { + /** USE AT OWN RISK! Synchronously force delete operations from the FSM. This will not perform any checks on which state the operation is in so may lead to data corruption or loss. It is recommended to first scale the number of replication engine workers to 0 before calling this endpoint to ensure no operations are in-flight. */ + post: operations['forceDeleteReplications']; }; '/replication/replicate/{id}': { - /** Returns the details of a replication operation for a given shard, identified by the provided replication operation id. */ + /** Fetches the current status and detailed information for a specific replication operation, identified by its unique ID. Optionally includes historical data of the operation's progress if requested. */ get: operations['replicationDetails']; + /** Removes a specific replication operation. If the operation is currently active, it will be cancelled and its resources cleaned up before the operation is deleted. */ + delete: operations['deleteReplication']; + }; + '/replication/replicate/list': { + /** Retrieves a list of currently registered replication operations, optionally filtered by collection, shard, or node ID. */ + get: operations['listReplication']; + }; + '/replication/replicate/{id}/cancel': { + /** Requests the cancellation of an active replication operation identified by its ID. The operation will be stopped, but its record will remain in the 'CANCELLED' state (can't be resumed) and will not be automatically deleted. */ + post: operations['cancelReplication']; + }; + '/replication/sharding-state': { + /** Fetches the current sharding state, including replica locations and statuses, for all collections or a specified collection. If a shard name is provided along with a collection, the state for that specific shard is returned. */ + get: operations['getCollectionShardingState']; }; '/users/own-info': { get: operations['getOwnInfo']; @@ -115,19 +135,19 @@ export interface paths { post: operations['objects.create']; }; '/objects/{id}': { - /** Get a specific object based on its UUID. Also available as Websocket bus. */ + /** Get a specific object based on its UUID. Also available as Websocket bus.

**Note**: This endpoint is deprecated and will be removed in a future version. Use the `/objects/{className}/{id}` endpoint instead. */ get: operations['objects.get']; - /** Updates an object based on its UUID. Given meta-data and schema values are validated. LastUpdateTime is set to the time this function is called. */ + /** Updates an object based on its UUID. Given meta-data and schema values are validated. LastUpdateTime is set to the time this function is called.

**Note**: This endpoint is deprecated and will be removed in a future version. Use the `/objects/{className}/{id}` endpoint instead. */ put: operations['objects.update']; - /** Deletes an object from the database based on its UUID. */ + /** Deletes an object from the database based on its UUID.

**Note**: This endpoint is deprecated and will be removed in a future version. Use the `/objects/{className}/{id}` endpoint instead. */ delete: operations['objects.delete']; - /** Checks if an object exists in the system based on its UUID. */ + /** Checks if an object exists in the system based on its UUID.

**Note**: This endpoint is deprecated and will be removed in a future version. Use the `/objects/{className}/{id}` endpoint instead. */ head: operations['objects.head']; - /** Update an object based on its UUID (using patch semantics). This method supports json-merge style patch semantics (RFC 7396). Provided meta-data and schema values are validated. LastUpdateTime is set to the time this function is called. */ + /** Update an object based on its UUID (using patch semantics). This method supports json-merge style patch semantics (RFC 7396). Provided meta-data and schema values are validated. LastUpdateTime is set to the time this function is called.

**Note**: This endpoint is deprecated and will be removed in a future version. Use the `/objects/{className}/{id}` endpoint instead. */ patch: operations['objects.patch']; }; '/objects/{className}/{id}': { - /** Get a data object based on its collection and UUID. Also available as Websocket bus. */ + /** Get a data object based on its collection and UUID. */ get: operations['objects.class.get']; /** Update an object based on its uuid and collection. This (`put`) method replaces the object with the provided object. */ put: operations['objects.class.put']; @@ -139,11 +159,11 @@ export interface paths { patch: operations['objects.class.patch']; }; '/objects/{id}/references/{propertyName}': { - /** Replace all references in cross-reference property of an object. */ + /** Replace all references in cross-reference property of an object.

**Note**: This endpoint is deprecated and will be removed in a future version. Use the `/objects/{className}/{id}/references/{propertyName}` endpoint instead. */ put: operations['objects.references.update']; - /** Add a cross-reference. */ + /** Add a cross-reference.

**Note**: This endpoint is deprecated and will be removed in a future version. Use the `/objects/{className}/{id}/references/{propertyName}` endpoint instead. */ post: operations['objects.references.create']; - /** Delete the single reference that is given in the body from the list of references that this property has. */ + /** Delete the single reference that is given in the body from the list of references that this property has.

**Note**: This endpoint is deprecated and will be removed in a future version. Use the `/objects/{className}/{id}/references/{propertyName}` endpoint instead. */ delete: operations['objects.references.delete']; }; '/objects/{className}/{id}/references/{propertyName}': { @@ -220,6 +240,20 @@ export interface paths { /** Check if a tenant exists for a specific class */ head: operations['tenant.exists']; }; + '/aliases': { + /** Retrieve a list of all aliases in the system. Results can be filtered by specifying a collection (class) name to get aliases for a specific collection only. */ + get: operations['aliases.get']; + /** Create a new alias mapping between an alias name and a collection (class). The alias acts as an alternative name for accessing the collection. */ + post: operations['aliases.create']; + }; + '/aliases/{aliasName}': { + /** Retrieve details about a specific alias by its name, including which collection (class) it points to. */ + get: operations['aliases.get.alias']; + /** Update an existing alias to point to a different collection (class). This allows you to redirect an alias from one collection to another without changing the alias name. */ + put: operations['aliases.update']; + /** Remove an existing alias from the system. This will delete the alias mapping but will not affect the underlying collection (class). */ + delete: operations['aliases.delete']; + }; '/backups/{backend}': { /** [Coming soon] List all backups in progress not implemented yet. */ get: operations['backups.list']; @@ -250,6 +284,9 @@ export interface paths { /** Returns node information for the nodes relevant to the collection. */ get: operations['nodes.get.class']; }; + '/tasks': { + get: operations['distributedTasks.get']; + }; '/classifications/': { /** Trigger a classification based on the specified params. Classifications will run in the background, use GET /classifications/ to retrieve the status of your classification. */ post: operations['classifications.post']; @@ -397,6 +434,32 @@ export interface definitions { */ collection?: string; }; + /** @description resources applicable for replicate actions */ + replicate?: { + /** + * @description string or regex. if a specific collection name, if left empty it will be ALL or * + * @default * + */ + collection?: string; + /** + * @description string or regex. if a specific shard name, if left empty it will be ALL or * + * @default * + */ + shard?: string; + }; + /** @description Resource definition for alias-related actions and permissions. Used to specify which aliases and collections can be accessed or modified. */ + aliases?: { + /** + * @description A string that specifies which collections this permission applies to. Can be an exact collection name or a regex pattern. The default value `*` applies the permission to all collections. + * @default * + */ + collection?: string; + /** + * @description A string that specifies which aliases this permission applies to. Can be an exact alias name or a regex pattern. The default value `*` applies the permission to all aliases. + * @default * + */ + alias?: string; + }; /** * @description allowed actions in weaviate. * @enum {string} @@ -425,7 +488,15 @@ export interface definitions { | 'create_tenants' | 'read_tenants' | 'update_tenants' - | 'delete_tenants'; + | 'delete_tenants' + | 'create_replicate' + | 'read_replicate' + | 'update_replicate' + | 'delete_replicate' + | 'create_aliases' + | 'read_aliases' + | 'update_aliases' + | 'delete_aliases'; }; /** @description list of roles */ RolesListResponse: definitions['Role'][]; @@ -678,57 +749,151 @@ export interface definitions { value?: { [key: string]: unknown }; merge?: definitions['Object']; }; - /** @description Request body to add a replica of given shard of a given collection */ + /** @description Specifies the parameters required to initiate a shard replica movement operation between two nodes for a given collection and shard. This request defines the source and target node, the collection and type of transfer. */ ReplicationReplicateReplicaRequest: { - /** @description The node containing the replica */ - sourceNodeName: string; - /** @description The node to add a copy of the replica on */ - destinationNodeName: string; - /** @description The collection name holding the shard */ - collectionId: string; - /** @description The shard id holding the replica to be copied */ - shardId: string; - }; - /** @description Request body to disable (soft-delete) a replica of given shard of a given collection */ + /** @description The name of the Weaviate node currently hosting the shard replica that needs to be moved or copied. */ + sourceNode: string; + /** @description The name of the Weaviate node where the new shard replica will be created as part of the movement or copy operation. */ + targetNode: string; + /** @description The name of the collection to which the target shard belongs. */ + collection: string; + /** @description The name of the shard whose replica is to be moved or copied. */ + shard: string; + /** + * @description Specifies the type of replication operation to perform. 'COPY' creates a new replica on the target node while keeping the source replica. 'MOVE' creates a new replica on the target node and then removes the source replica upon successful completion. Defaults to 'COPY' if omitted. + * @default COPY + * @enum {string} + */ + type?: 'COPY' | 'MOVE'; + }; + /** @description Contains the unique identifier for a successfully initiated asynchronous replica movement operation. This ID can be used to track the progress of the operation. */ + ReplicationReplicateReplicaResponse: { + /** + * Format: uuid + * @description The unique identifier (ID) assigned to the registered replication operation. + */ + id: string; + }; + /** @description Provides the detailed sharding state for one or more collections, including the distribution of shards and their replicas across the cluster nodes. */ + ReplicationShardingStateResponse: { + shardingState?: definitions['ReplicationShardingState']; + }; + /** @description Specifies the parameters required to mark a specific shard replica as inactive (soft-delete) on a particular node. This action typically prevents the replica from serving requests but does not immediately remove its data. */ ReplicationDisableReplicaRequest: { - /** @description The node containing the replica to be disabled */ - nodeName: string; - /** @description The collection name holding the replica to be disabled */ - collectionId: string; - /** @description The shard id holding the replica to be disabled */ - shardId: string; - }; - /** @description Request body to delete a replica of given shard of a given collection */ + /** @description The name of the Weaviate node hosting the shard replica that is to be disabled. */ + node: string; + /** @description The name of the collection to which the shard replica belongs. */ + collection: string; + /** @description The ID of the shard whose replica is to be disabled. */ + shard: string; + }; + /** @description Specifies the parameters required to permanently delete a specific shard replica from a particular node. This action will remove the replica's data from the node. */ ReplicationDeleteReplicaRequest: { - /** @description The node containing the replica to be deleted */ - nodeName: string; - /** @description The collection name holding the replica to be delete */ - collectionId: string; - /** @description The shard id holding the replica to be deleted */ - shardId: string; - }; - /** @description The current status and details of a replication operation, including information about the resources involved in the replication process. */ + /** @description The name of the Weaviate node from which the shard replica will be deleted. */ + node: string; + /** @description The name of the collection to which the shard replica belongs. */ + collection: string; + /** @description The ID of the shard whose replica is to be deleted. */ + shard: string; + }; + /** @description Represents a shard and lists the nodes that currently host its replicas. */ + ReplicationShardReplicas: { + shard?: string; + replicas?: string[]; + }; + /** @description Details the sharding layout for a specific collection, mapping each shard to its set of replicas across the cluster. */ + ReplicationShardingState: { + /** @description The name of the collection. */ + collection?: string; + /** @description An array detailing each shard within the collection and the nodes hosting its replicas. */ + shards?: definitions['ReplicationShardReplicas'][]; + }; + /** @description Represents an error encountered during a replication operation, including its timestamp and a human-readable message. */ + ReplicationReplicateDetailsReplicaStatusError: { + /** + * Format: int64 + * @description The unix timestamp in ms when the error occurred. This is an approximate time and so should not be used for precise timing. + */ + whenErroredUnixMs?: number; + /** @description A human-readable message describing the error. */ + message?: string; + }; + /** @description Represents the current or historical status of a shard replica involved in a replication operation, including its operational state and any associated errors. */ + ReplicationReplicateDetailsReplicaStatus: { + /** + * @description The current operational state of the replica during the replication process. + * @enum {string} + */ + state?: 'REGISTERED' | 'HYDRATING' | 'FINALIZING' | 'DEHYDRATING' | 'READY' | 'CANCELLED'; + /** + * Format: int64 + * @description The UNIX timestamp in ms when this state was first entered. This is an approximate time and so should not be used for precise timing. + */ + whenStartedUnixMs?: number; + /** @description A list of error messages encountered by this replica during the replication operation, if any. */ + errors?: definitions['ReplicationReplicateDetailsReplicaStatusError'][]; + }; + /** @description Provides a comprehensive overview of a specific replication operation, detailing its unique ID, the involved collection, shard, source and target nodes, transfer type, current status, and optionally, its status history. */ ReplicationReplicateDetailsReplicaResponse: { - /** @description The unique id of the replication operation. */ + /** + * Format: uuid + * @description The unique identifier (ID) of this specific replication operation. + */ id: string; - /** @description The id of the shard to collect replication details for. */ - shardId: string; - /** @description The name of the collection holding data being replicated. */ + /** @description The name of the shard involved in this replication operation. */ + shard: string; + /** @description The name of the collection to which the shard being replicated belongs. */ collection: string; - /** @description The id of the node where the source replica is allocated. */ - sourceNodeId: string; - /** @description The id of the node where the target replica is allocated. */ - targetNodeId: string; + /** @description The identifier of the node from which the replica is being moved or copied (the source node). */ + sourceNode: string; + /** @description The identifier of the node to which the replica is being moved or copied (the target node). */ + targetNode: string; /** - * @description The current status of the replication operation, indicating the replication phase the operation is in. + * @description Indicates whether the operation is a 'COPY' (source replica remains) or a 'MOVE' (source replica is removed after successful transfer). * @enum {string} */ - status: - | 'READY' - | 'INDEXING' - | 'REPLICATION_FINALIZING' - | 'REPLICATION_HYDRATING' - | 'REPLICATION_DEHYDRATING'; + type: 'COPY' | 'MOVE'; + /** @description Whether the replica operation is uncancelable. */ + uncancelable?: boolean; + /** @description Whether the replica operation is scheduled for cancellation. */ + scheduledForCancel?: boolean; + /** @description Whether the replica operation is scheduled for deletion. */ + scheduledForDelete?: boolean; + /** @description An object detailing the current operational state of the replica movement and any errors encountered. */ + status: definitions['ReplicationReplicateDetailsReplicaStatus']; + /** @description An array detailing the historical sequence of statuses the replication operation has transitioned through, if requested and available. */ + statusHistory?: definitions['ReplicationReplicateDetailsReplicaStatus'][]; + /** + * Format: int64 + * @description The UNIX timestamp in ms when the replication operation was initiated. This is an approximate time and so should not be used for precise timing. + */ + whenStartedUnixMs?: number; + }; + /** @description Specifies the parameters available when force deleting replication operations. */ + ReplicationReplicateForceDeleteRequest: { + /** + * Format: uuid + * @description The unique identifier (ID) of the replication operation to be forcefully deleted. + */ + id?: string; + /** @description The name of the collection to which the shard being replicated belongs. */ + collection?: string; + /** @description The identifier of the shard involved in the replication operations. */ + shard?: string; + /** @description The name of the target node where the replication operations are registered. */ + node?: string; + /** + * @description If true, the operation will not actually delete anything but will return the expected outcome of the deletion. + * @default false + */ + dryRun?: boolean; + }; + /** @description Provides the UUIDs that were successfully force deleted as part of the replication operation. If dryRun is true, this will return the expected outcome without actually deleting anything. */ + ReplicationReplicateForceDeleteResponse: { + /** @description The unique identifiers (IDs) of the replication operations that were forcefully deleted. */ + deleted?: string[]; + /** @description Indicates whether the operation was a dry run (true) or an actual deletion (false). */ + dryRun?: boolean; }; /** @description A single peer in the network. */ PeerUpdate: { @@ -957,6 +1122,18 @@ export interface definitions { * @default 50 */ CPUPercentage?: number; + /** + * @description How roles should be restored + * @default noRestore + * @enum {string} + */ + rolesOptions?: 'noRestore' | 'all'; + /** + * @description How users should be restored + * @default noRestore + * @enum {string} + */ + usersOptions?: 'noRestore' | 'all'; }; /** @description Request body for creating a backup of a set of classes */ BackupCreateRequest: { @@ -1088,6 +1265,33 @@ export interface definitions { vectorQueueLength?: number; /** @description The load status of the shard. */ loaded?: boolean; + /** @description The status of the async replication. */ + asyncReplicationStatus?: definitions['AsyncReplicationStatus'][]; + /** + * Format: int64 + * @description Number of replicas for the shard. + */ + numberOfReplicas?: unknown; + /** + * Format: int64 + * @description Minimum number of replicas for the shard. + */ + replicationFactor?: unknown; + }; + /** @description The status of the async replication. */ + AsyncReplicationStatus: { + /** + * Format: uint64 + * @description The number of objects propagated in the most recent iteration. + */ + objectsPropagated?: number; + /** + * Format: int64 + * @description The start time of the most recent iteration. + */ + startDiffTimeUnixMillis?: number; + /** @description The target node of the replication, if set, otherwise empty. */ + targetNode?: string; }; /** @description The definition of a backup node status response body */ NodeStatus: { @@ -1114,6 +1318,33 @@ export interface definitions { NodesStatusResponse: { nodes?: definitions['NodeStatus'][]; }; + /** @description Distributed task metadata. */ + DistributedTask: { + /** @description The ID of the task. */ + id?: string; + /** @description The version of the task. */ + version?: number; + /** @description The status of the task. */ + status?: string; + /** + * Format: date-time + * @description The time when the task was created. + */ + startedAt?: string; + /** + * Format: date-time + * @description The time when the task was finished. + */ + finishedAt?: string; + /** @description The nodes that finished the task. */ + finishedNodes?: string[]; + /** @description The high level reason why the task failed. */ + error?: string; + /** @description The payload of the task. */ + payload?: { [key: string]: unknown }; + }; + /** @description Active distributed tasks by namespace. */ + DistributedTasks: { [key: string]: definitions['DistributedTask'][] }; /** @description The definition of Raft statistics. */ RaftStatistics: { appliedIndex?: string; @@ -1670,10 +1901,17 @@ export interface definitions { | 'FREEZING' | 'UNFREEZING'; }; - /** @description attributes representing a single tenant response within weaviate */ - TenantResponse: definitions['Tenant'] & { - /** @description The list of nodes that owns that tenant data. */ - belongsToNodes?: string[]; + /** @description Represents the mapping between an alias name and a collection. An alias provides an alternative name for accessing a collection. */ + Alias: { + /** @description The unique name of the alias that serves as an alternative identifier for the collection. */ + alias?: string; + /** @description The name of the collection (class) to which this alias is mapped. */ + class?: string; + }; + /** @description Response object containing a list of alias mappings. */ + AliasResponse: { + /** @description Array of alias objects, each containing an alias-to-collection mapping. */ + aliases?: definitions['Alias'][]; }; } @@ -1740,15 +1978,46 @@ export interface operations { 503: unknown; }; }; + /** Begins an asynchronous operation to move or copy a specific shard replica from its current node to a designated target node. The operation involves copying data, synchronizing, and potentially decommissioning the source replica. */ replicate: { parameters: { body: { body: definitions['ReplicationReplicateReplicaRequest']; }; }; + responses: { + /** Replication operation registered successfully. ID of the operation is returned. */ + 200: { + schema: definitions['ReplicationReplicateReplicaResponse']; + }; + /** Malformed request. */ + 400: { + schema: definitions['ErrorResponse']; + }; + /** Unauthorized or invalid credentials. */ + 401: unknown; + /** Forbidden */ + 403: { + schema: definitions['ErrorResponse']; + }; + /** Request body is well-formed (i.e., syntactically correct), but semantically erroneous. */ + 422: { + schema: definitions['ErrorResponse']; + }; + /** An error has occurred while trying to fulfill the request. Most likely the ErrorResponse will contain more information about the error. */ + 500: { + schema: definitions['ErrorResponse']; + }; + /** Replica movement operations are disabled. */ + 501: { + schema: definitions['ErrorResponse']; + }; + }; + }; + deleteAllReplications: { responses: { /** Replication operation registered successfully */ - 200: unknown; + 204: never; /** Malformed request. */ 400: { schema: definitions['ErrorResponse']; @@ -1767,22 +2036,140 @@ export interface operations { 500: { schema: definitions['ErrorResponse']; }; + /** Replica movement operations are disabled. */ + 501: { + schema: definitions['ErrorResponse']; + }; }; }; - /** Returns the details of a replication operation for a given shard, identified by the provided replication operation id. */ + /** USE AT OWN RISK! Synchronously force delete operations from the FSM. This will not perform any checks on which state the operation is in so may lead to data corruption or loss. It is recommended to first scale the number of replication engine workers to 0 before calling this endpoint to ensure no operations are in-flight. */ + forceDeleteReplications: { + parameters: { + body: { + body?: definitions['ReplicationReplicateForceDeleteRequest']; + }; + }; + responses: { + /** Replication operations force deleted successfully. */ + 200: { + schema: definitions['ReplicationReplicateForceDeleteResponse']; + }; + /** Malformed request. */ + 400: { + schema: definitions['ErrorResponse']; + }; + /** Unauthorized or invalid credentials. */ + 401: unknown; + /** Forbidden */ + 403: { + schema: definitions['ErrorResponse']; + }; + /** Request body is well-formed (i.e., syntactically correct), but semantically erroneous. */ + 422: { + schema: definitions['ErrorResponse']; + }; + /** An error has occurred while trying to fulfill the request. Most likely the ErrorResponse will contain more information about the error. */ + 500: { + schema: definitions['ErrorResponse']; + }; + }; + }; + /** Fetches the current status and detailed information for a specific replication operation, identified by its unique ID. Optionally includes historical data of the operation's progress if requested. */ replicationDetails: { parameters: { path: { - /** The replication operation id to get details for. */ + /** The ID of the replication operation to get details for. */ id: string; }; + query: { + /** Whether to include the history of the replication operation. */ + includeHistory?: boolean; + }; }; responses: { /** The details of the replication operation. */ 200: { schema: definitions['ReplicationReplicateDetailsReplicaResponse']; }; - /** Malformed request. */ + /** Unauthorized or invalid credentials. */ + 401: unknown; + /** Forbidden. */ + 403: { + schema: definitions['ErrorResponse']; + }; + /** Shard replica operation not found. */ + 404: unknown; + /** Request body is well-formed (i.e., syntactically correct), but semantically erroneous. */ + 422: { + schema: definitions['ErrorResponse']; + }; + /** An error has occurred while trying to fulfill the request. Most likely the ErrorResponse will contain more information about the error. */ + 500: { + schema: definitions['ErrorResponse']; + }; + /** Replica movement operations are disabled. */ + 501: { + schema: definitions['ErrorResponse']; + }; + }; + }; + /** Removes a specific replication operation. If the operation is currently active, it will be cancelled and its resources cleaned up before the operation is deleted. */ + deleteReplication: { + parameters: { + path: { + /** The ID of the replication operation to delete. */ + id: string; + }; + }; + responses: { + /** Successfully deleted. */ + 204: never; + /** Unauthorized or invalid credentials. */ + 401: unknown; + /** Forbidden. */ + 403: { + schema: definitions['ErrorResponse']; + }; + /** Shard replica operation not found. */ + 404: unknown; + /** The operation is not in a deletable state, e.g. it is a MOVE op in the DEHYDRATING state. */ + 409: { + schema: definitions['ErrorResponse']; + }; + /** Request body is well-formed (i.e., syntactically correct), but semantically erroneous. */ + 422: { + schema: definitions['ErrorResponse']; + }; + /** An error has occurred while trying to fulfill the request. Most likely the ErrorResponse will contain more information about the error. */ + 500: { + schema: definitions['ErrorResponse']; + }; + /** Replica movement operations are disabled. */ + 501: { + schema: definitions['ErrorResponse']; + }; + }; + }; + /** Retrieves a list of currently registered replication operations, optionally filtered by collection, shard, or node ID. */ + listReplication: { + parameters: { + query: { + /** The name of the target node to get details for. */ + targetNode?: string; + /** The name of the collection to get details for. */ + collection?: string; + /** The shard to get details for. */ + shard?: string; + /** Whether to include the history of the replication operation. */ + includeHistory?: boolean; + }; + }; + responses: { + /** The details of the replication operations. */ + 200: { + schema: definitions['ReplicationReplicateDetailsReplicaResponse'][]; + }; + /** Bad request. */ 400: { schema: definitions['ErrorResponse']; }; @@ -1792,12 +2179,90 @@ export interface operations { 403: { schema: definitions['ErrorResponse']; }; - /** Shard replica operation not found */ + /** An error has occurred while trying to fulfill the request. Most likely the ErrorResponse will contain more information about the error. */ + 500: { + schema: definitions['ErrorResponse']; + }; + /** Replica movement operations are disabled. */ + 501: { + schema: definitions['ErrorResponse']; + }; + }; + }; + /** Requests the cancellation of an active replication operation identified by its ID. The operation will be stopped, but its record will remain in the 'CANCELLED' state (can't be resumed) and will not be automatically deleted. */ + cancelReplication: { + parameters: { + path: { + /** The ID of the replication operation to cancel. */ + id: string; + }; + }; + responses: { + /** Successfully cancelled. */ + 204: never; + /** Unauthorized or invalid credentials. */ + 401: unknown; + /** Forbidden */ + 403: { + schema: definitions['ErrorResponse']; + }; + /** Shard replica operation not found. */ 404: unknown; + /** The operation is not in a cancellable state, e.g. it is READY or is a MOVE op in the DEHYDRATING state. */ + 409: { + schema: definitions['ErrorResponse']; + }; + /** Request body is well-formed (i.e., syntactically correct), but semantically erroneous. */ + 422: { + schema: definitions['ErrorResponse']; + }; /** An error has occurred while trying to fulfill the request. Most likely the ErrorResponse will contain more information about the error. */ 500: { schema: definitions['ErrorResponse']; }; + /** Replica movement operations are disabled. */ + 501: { + schema: definitions['ErrorResponse']; + }; + }; + }; + /** Fetches the current sharding state, including replica locations and statuses, for all collections or a specified collection. If a shard name is provided along with a collection, the state for that specific shard is returned. */ + getCollectionShardingState: { + parameters: { + query: { + /** The collection name to get the sharding state for. */ + collection?: string; + /** The shard to get the sharding state for. */ + shard?: string; + }; + }; + responses: { + /** Successfully retrieved sharding state. */ + 200: { + schema: definitions['ReplicationShardingStateResponse']; + }; + /** Bad request. */ + 400: { + schema: definitions['ErrorResponse']; + }; + /** Unauthorized or invalid credentials. */ + 401: unknown; + /** Forbidden */ + 403: { + schema: definitions['ErrorResponse']; + }; + /** Collection or shard not found. */ + 404: { + schema: definitions['ErrorResponse']; + }; + /** An error has occurred while trying to fulfill the request. Most likely the ErrorResponse will contain more information about the error. */ + 500: { + schema: definitions['ErrorResponse']; + }; + /** Replica movement operations are disabled. */ + 501: { + schema: definitions['ErrorResponse']; + }; }; }; getOwnInfo: { @@ -1812,6 +2277,10 @@ export interface operations { 500: { schema: definitions['ErrorResponse']; }; + /** Replica movement operations are disabled. */ + 501: { + schema: definitions['ErrorResponse']; + }; }; }; listAllUsers: { @@ -1878,6 +2347,20 @@ export interface operations { /** user id */ user_id: string; }; + body: { + body?: { + /** + * @description EXPERIMENTAL, DONT USE. THIS WILL BE REMOVED AGAIN. - import api key from static user + * @default false + */ + import?: boolean; + /** + * Format: date-time + * @description EXPERIMENTAL, DONT USE. THIS WILL BE REMOVED AGAIN. - set the given time as creation time + */ + createTime?: string; + }; + }; }; responses: { /** User created successfully */ @@ -1894,6 +2377,10 @@ export interface operations { 403: { schema: definitions['ErrorResponse']; }; + /** user not found */ + 404: { + schema: definitions['ErrorResponse']; + }; /** User already exists */ 409: { schema: definitions['ErrorResponse']; @@ -2641,7 +3128,7 @@ export interface operations { }; }; }; - /** Get a specific object based on its UUID. Also available as Websocket bus. */ + /** Get a specific object based on its UUID. Also available as Websocket bus.

**Note**: This endpoint is deprecated and will be removed in a future version. Use the `/objects/{className}/{id}` endpoint instead. */ 'objects.get': { parameters: { path: { @@ -2676,7 +3163,7 @@ export interface operations { }; }; }; - /** Updates an object based on its UUID. Given meta-data and schema values are validated. LastUpdateTime is set to the time this function is called. */ + /** Updates an object based on its UUID. Given meta-data and schema values are validated. LastUpdateTime is set to the time this function is called.

**Note**: This endpoint is deprecated and will be removed in a future version. Use the `/objects/{className}/{id}` endpoint instead. */ 'objects.update': { parameters: { path: { @@ -2714,7 +3201,7 @@ export interface operations { }; }; }; - /** Deletes an object from the database based on its UUID. */ + /** Deletes an object from the database based on its UUID.

**Note**: This endpoint is deprecated and will be removed in a future version. Use the `/objects/{className}/{id}` endpoint instead. */ 'objects.delete': { parameters: { path: { @@ -2745,7 +3232,7 @@ export interface operations { }; }; }; - /** Checks if an object exists in the system based on its UUID. */ + /** Checks if an object exists in the system based on its UUID.

**Note**: This endpoint is deprecated and will be removed in a future version. Use the `/objects/{className}/{id}` endpoint instead. */ 'objects.head': { parameters: { path: { @@ -2770,7 +3257,7 @@ export interface operations { }; }; }; - /** Update an object based on its UUID (using patch semantics). This method supports json-merge style patch semantics (RFC 7396). Provided meta-data and schema values are validated. LastUpdateTime is set to the time this function is called. */ + /** Update an object based on its UUID (using patch semantics). This method supports json-merge style patch semantics (RFC 7396). Provided meta-data and schema values are validated. LastUpdateTime is set to the time this function is called.

**Note**: This endpoint is deprecated and will be removed in a future version. Use the `/objects/{className}/{id}` endpoint instead. */ 'objects.patch': { parameters: { path: { @@ -2809,7 +3296,7 @@ export interface operations { }; }; }; - /** Get a data object based on its collection and UUID. Also available as Websocket bus. */ + /** Get a data object based on its collection and UUID. */ 'objects.class.get': { parameters: { path: { @@ -3014,7 +3501,7 @@ export interface operations { }; }; }; - /** Replace all references in cross-reference property of an object. */ + /** Replace all references in cross-reference property of an object.

**Note**: This endpoint is deprecated and will be removed in a future version. Use the `/objects/{className}/{id}/references/{propertyName}` endpoint instead. */ 'objects.references.update': { parameters: { path: { @@ -3050,7 +3537,7 @@ export interface operations { }; }; }; - /** Add a cross-reference. */ + /** Add a cross-reference.

**Note**: This endpoint is deprecated and will be removed in a future version. Use the `/objects/{className}/{id}/references/{propertyName}` endpoint instead. */ 'objects.references.create': { parameters: { path: { @@ -3086,7 +3573,7 @@ export interface operations { }; }; }; - /** Delete the single reference that is given in the body from the list of references that this property has. */ + /** Delete the single reference that is given in the body from the list of references that this property has.

**Note**: This endpoint is deprecated and will be removed in a future version. Use the `/objects/{className}/{id}/references/{propertyName}` endpoint instead. */ 'objects.references.delete': { parameters: { path: { @@ -3858,7 +4345,7 @@ export interface operations { responses: { /** load the tenant given the specified class */ 200: { - schema: definitions['TenantResponse']; + schema: definitions['Tenant']; }; /** Unauthorized or invalid credentials. */ 401: unknown; @@ -3911,6 +4398,163 @@ export interface operations { }; }; }; + /** Retrieve a list of all aliases in the system. Results can be filtered by specifying a collection (class) name to get aliases for a specific collection only. */ + 'aliases.get': { + parameters: { + query: { + /** Optional filter to retrieve aliases for a specific collection (class) only. If not provided, returns all aliases. */ + class?: string; + }; + }; + responses: { + /** Successfully retrieved the list of aliases */ + 200: { + schema: definitions['AliasResponse']; + }; + /** Unauthorized or invalid credentials. */ + 401: unknown; + /** Forbidden */ + 403: { + schema: definitions['ErrorResponse']; + }; + /** Invalid collection (class) parameter provided */ + 422: { + schema: definitions['ErrorResponse']; + }; + /** An error has occurred while trying to fulfill the request. Most likely the ErrorResponse will contain more information about the error. */ + 500: { + schema: definitions['ErrorResponse']; + }; + }; + }; + /** Create a new alias mapping between an alias name and a collection (class). The alias acts as an alternative name for accessing the collection. */ + 'aliases.create': { + parameters: { + body: { + body: definitions['Alias']; + }; + }; + responses: { + /** Successfully created a new alias for the specified collection (class) */ + 200: { + schema: definitions['Alias']; + }; + /** Unauthorized or invalid credentials. */ + 401: unknown; + /** Forbidden */ + 403: { + schema: definitions['ErrorResponse']; + }; + /** Invalid create alias request. */ + 422: { + schema: definitions['ErrorResponse']; + }; + /** An error has occurred while trying to fulfill the request. Most likely the ErrorResponse will contain more information about the error. */ + 500: { + schema: definitions['ErrorResponse']; + }; + }; + }; + /** Retrieve details about a specific alias by its name, including which collection (class) it points to. */ + 'aliases.get.alias': { + parameters: { + path: { + aliasName: string; + }; + }; + responses: { + /** Successfully retrieved the alias details. */ + 200: { + schema: definitions['Alias']; + }; + /** Unauthorized or invalid credentials. */ + 401: unknown; + /** Forbidden */ + 403: { + schema: definitions['ErrorResponse']; + }; + /** Not Found - Alias does not exist */ + 404: { + schema: definitions['ErrorResponse']; + }; + /** Invalid alias name provided. */ + 422: { + schema: definitions['ErrorResponse']; + }; + /** An error has occurred while trying to fulfill the request. Most likely the ErrorResponse will contain more information about the error. */ + 500: { + schema: definitions['ErrorResponse']; + }; + }; + }; + /** Update an existing alias to point to a different collection (class). This allows you to redirect an alias from one collection to another without changing the alias name. */ + 'aliases.update': { + parameters: { + path: { + aliasName: string; + }; + body: { + body: { + /** @description The new collection (class) that the alias should point to. */ + class?: string; + }; + }; + }; + responses: { + /** Successfully updated the alias to point to the new collection (class). */ + 200: { + schema: definitions['Alias']; + }; + /** Unauthorized or invalid credentials. */ + 401: unknown; + /** Forbidden */ + 403: { + schema: definitions['ErrorResponse']; + }; + /** Not Found - Alias does not exist */ + 404: { + schema: definitions['ErrorResponse']; + }; + /** Invalid update alias request. */ + 422: { + schema: definitions['ErrorResponse']; + }; + /** An error has occurred while trying to fulfill the request. Most likely the ErrorResponse will contain more information about the error. */ + 500: { + schema: definitions['ErrorResponse']; + }; + }; + }; + /** Remove an existing alias from the system. This will delete the alias mapping but will not affect the underlying collection (class). */ + 'aliases.delete': { + parameters: { + path: { + aliasName: string; + }; + }; + responses: { + /** Successfully deleted the alias. */ + 204: never; + /** Unauthorized or invalid credentials. */ + 401: unknown; + /** Forbidden */ + 403: { + schema: definitions['ErrorResponse']; + }; + /** Not Found - Alias does not exist */ + 404: { + schema: definitions['ErrorResponse']; + }; + /** Invalid delete alias request. */ + 422: { + schema: definitions['ErrorResponse']; + }; + /** An error has occurred while trying to fulfill the request. Most likely the ErrorResponse will contain more information about the error. */ + 500: { + schema: definitions['ErrorResponse']; + }; + }; + }; /** [Coming soon] List all backups in progress not implemented yet. */ 'backups.list': { parameters: { @@ -4186,6 +4830,7 @@ export interface operations { className: string; }; query: { + shardName?: string; /** Controls the verbosity of the output, possible values are: "minimal", "verbose". Defaults to "minimal". */ output?: parameters['CommonOutputVerbosityParameterQuery']; }; @@ -4215,6 +4860,22 @@ export interface operations { }; }; }; + 'distributedTasks.get': { + responses: { + /** Distributed tasks successfully returned */ + 200: { + schema: definitions['DistributedTasks']; + }; + /** Unauthorized or invalid credentials. */ + 403: { + schema: definitions['ErrorResponse']; + }; + /** An error has occurred while trying to fulfill the request. Most likely the ErrorResponse will contain more information about the error. */ + 500: { + schema: definitions['ErrorResponse']; + }; + }; + }; /** Trigger a classification based on the specified params. Classifications will run in the background, use GET /classifications/ to retrieve the status of your classification. */ 'classifications.post': { parameters: { diff --git a/src/openapi/types.ts b/src/openapi/types.ts index 59e6e7d1..b935baa9 100644 --- a/src/openapi/types.ts +++ b/src/openapi/types.ts @@ -74,3 +74,8 @@ export type WeaviateUserType = definitions['UserTypeOutput']; export type WeaviateUserTypeInternal = definitions['UserTypeInput']; export type WeaviateUserTypeDB = definitions['DBUserInfo']['dbUserType']; export type WeaviateAssignedUser = operations['getUsersForRole']['responses']['200']['schema'][0]; +// Alias +export type WeaviateAlias = definitions['Alias']; +export type WeaviateAliasResponse = { + aliases?: Required[0]>[] | undefined; +}; diff --git a/src/roles/index.ts b/src/roles/index.ts index 68dd7e4f..96a52b57 100644 --- a/src/roles/index.ts +++ b/src/roles/index.ts @@ -5,6 +5,7 @@ import { Role as WeaviateRole, } from '../openapi/types.js'; import { + AliasPermission, BackupsPermission, ClusterPermission, CollectionsPermission, @@ -150,6 +151,33 @@ const roles = (connection: ConnectionREST): Roles => { }; export const permissions = { + /** + * Create a set of permissions specific to Weaviate's collection aliasing functionality. + * + * @param {string | string[]} [args.alias] Aliases that will be associated with these permissions. + * @returns {AliasPermission[]} The permissions for the specified aliases. + */ + aliases: (args: { + alias: string | string[]; + collection: string | string[]; + create?: boolean; + read?: boolean; + update?: boolean; + delete?: boolean; + }): AliasPermission[] => { + const aliases = Array.isArray(args.alias) ? args.alias : [args.alias]; + const collections = Array.isArray(args.collection) ? args.collection : [args.collection]; + const combinations = aliases.flatMap((alias) => collections.map((collection) => ({ alias, collection }))); + return combinations.map(({ collection, alias }) => { + const out: AliasPermission = { alias, collection, actions: [] }; + if (args.create) out.actions.push('create_aliases'); + if (args.read) out.actions.push('read_aliases'); + if (args.update) out.actions.push('update_aliases'); + if (args.delete) out.actions.push('delete_aliases'); + return out; + }); + }, + /** * Create a set of permissions specific to Weaviate's backup functionality. * diff --git a/src/roles/integration.test.ts b/src/roles/integration.test.ts index c5886b57..c4d3ea33 100644 --- a/src/roles/integration.test.ts +++ b/src/roles/integration.test.ts @@ -16,9 +16,11 @@ type TestCase = { roleName: string; permissions: Permission[]; expected: Role; + requireVersion?: [number, number, number]; }; const emptyPermissions = { + aliasPermissions: [], backupsPermissions: [], clusterPermissions: [], collectionsPermissions: [], @@ -277,6 +279,23 @@ const testCases: TestCase[] = [ usersPermissions: [{ users: 'some-user', actions: ['assign_and_revoke_users', 'read_users'] }], }, }, + { + roleName: 'aliases', + requireVersion: [1, 32, 0], + permissions: weaviate.permissions.aliases({ + alias: 'SomeAlias', + collection: 'SomeCollection', + create: true, + delete: true, + }), + expected: { + name: 'aliases', + ...emptyPermissions, + aliasPermissions: [ + { alias: 'SomeAlias', collection: 'SomeCollection', actions: ['create_aliases', 'delete_aliases'] }, + ], + }, + }, ]; requireAtLeast(1, 29, 0).describe('Integration testing of the roles namespace', () => { @@ -345,11 +364,14 @@ requireAtLeast(1, 29, 0).describe('Integration testing of the roles namespace', describe('should be able to create roles using the permissions factory', () => { testCases.forEach((testCase) => { - it(`with ${testCase.roleName} permissions`, async () => { - await client.roles.create(testCase.roleName, testCase.permissions); - const role = await client.roles.byName(testCase.roleName); - expect(role).toEqual(testCase.expected); - }); + (testCase.requireVersion !== undefined ? requireAtLeast(...testCase.requireVersion).it : it)( + `with ${testCase.roleName} permissions`, + async () => { + await client.roles.create(testCase.roleName, testCase.permissions); + const role = await client.roles.byName(testCase.roleName); + expect(role).toEqual(testCase.expected); + } + ); }); }); diff --git a/src/roles/types.ts b/src/roles/types.ts index 93273521..5b94d8fb 100644 --- a/src/roles/types.ts +++ b/src/roles/types.ts @@ -1,5 +1,9 @@ import { Action, WeaviateUserType } from '../openapi/types.js'; +export type AliasAction = Extract< + Action, + 'create_aliases' | 'read_aliases' | 'update_aliases' | 'delete_aliases' +>; export type BackupsAction = Extract; export type ClusterAction = Extract; export type CollectionsAction = Extract< @@ -27,6 +31,12 @@ export type UserAssignment = { userType: WeaviateUserType; }; +export type AliasPermission = { + alias: string; + collection: string; + actions: AliasAction[]; +}; + export type BackupsPermission = { collection: string; actions: BackupsAction[]; @@ -71,6 +81,7 @@ export type UsersPermission = { export type Role = { name: string; + aliasPermissions: AliasPermission[]; backupsPermissions: BackupsPermission[]; clusterPermissions: ClusterPermission[]; collectionsPermissions: CollectionsPermission[]; @@ -82,6 +93,7 @@ export type Role = { }; export type Permission = + | AliasPermission | BackupsPermission | ClusterPermission | CollectionsPermission diff --git a/src/roles/util.ts b/src/roles/util.ts index 7bf42444..dd5f4f11 100644 --- a/src/roles/util.ts +++ b/src/roles/util.ts @@ -7,6 +7,8 @@ import { } from '../openapi/types.js'; import { User, UserDB } from '../users/types.js'; import { + AliasAction, + AliasPermission, BackupsAction, BackupsPermission, ClusterAction, @@ -35,6 +37,14 @@ const ZERO_TIME = '0001-01-01T00:00:00.000Z'; export class PermissionGuards { private static includes = (permission: Permission, ...actions: A[]): boolean => actions.filter((a) => Array.from(permission.actions).includes(a)).length > 0; + static isAlias = (permission: Permission): permission is AliasPermission => + PermissionGuards.includes( + permission, + 'create_aliases', + 'read_aliases', + 'update_aliases', + 'delete_aliases' + ); static isBackups = (permission: Permission): permission is BackupsPermission => PermissionGuards.includes(permission, 'manage_backups'); static isCluster = (permission: Permission): permission is ClusterPermission => @@ -94,6 +104,12 @@ export class Map { !Array.isArray(permissions) ? [permissions] : permissions.flat(2); static permissionToWeaviate = (permission: Permission): WeaviatePermission[] => { + if (PermissionGuards.isAlias(permission)) { + return Array.from(permission.actions).map((action) => ({ + aliases: permission, + action, + })); + } if (PermissionGuards.isBackups(permission)) { return Array.from(permission.actions).map((action) => ({ backups: permission, @@ -178,6 +194,7 @@ class PermissionsMapping { private constructor(role: WeaviateRole) { this.mappings = { + aliases: {}, backups: {}, cluster: {}, collections: {}, @@ -200,6 +217,7 @@ class PermissionsMapping { } return { name: this.role.name, + aliasPermissions: Object.values(this.mappings.aliases), backupsPermissions: Object.values(this.mappings.backups), clusterPermissions: Object.values(this.mappings.cluster), collectionsPermissions: Object.values(this.mappings.collections), @@ -211,6 +229,17 @@ class PermissionsMapping { }; }; + private aliases = (permission: WeaviatePermission) => { + if (permission.aliases !== undefined) { + const { alias, collection } = permission.aliases; + if (alias === undefined) throw new Error('Alias permission missing an alias'); + if (this.mappings.aliases[alias] === undefined) { + this.mappings.aliases[alias] = { alias, collection: collection || '*', actions: [] }; + } + this.mappings.aliases[alias].actions.push(permission.action as AliasAction); + } + }; + private backups = (permission: WeaviatePermission) => { if (permission.backups !== undefined) { const key = permission.backups.collection; @@ -295,6 +324,7 @@ class PermissionsMapping { }; private permissionFromWeaviate = (permission: WeaviatePermission) => { + this.aliases(permission); this.backups(permission); this.cluster(permission); this.collections(permission); @@ -307,6 +337,7 @@ class PermissionsMapping { } type PermissionMappings = { + aliases: Record; backups: Record; cluster: Record; collections: Record;