Skip to content

Commit 43679b7

Browse files
authored
Merge pull request #316 from weaviate/feat/collection-alias
Add `alias` namespace with functions for managing collection aliases.
2 parents cf877ad + f2ba029 commit 43679b7

File tree

9 files changed

+256
-5
lines changed

9 files changed

+256
-5
lines changed

src/alias/index.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { ConnectionREST } from '../index.js';
2+
import { WeaviateAlias, WeaviateAliasResponse } from '../openapi/types.js';
3+
import { Alias, AliasListAllOptions, CreateAliasArgs, UpdateAliasArgs } from './types.js';
4+
5+
export interface Aliases {
6+
/**
7+
* Create alias for a collection.
8+
*
9+
* The collection must exist prior to aliasing it.
10+
* One alias cannot be created for multiple collections simultaneously.
11+
*
12+
* @param {string} args.collection Original collection name.
13+
* @param {string} args.alias Alias for collection.
14+
* @returns {Promise<void>} Awaitable promise.
15+
* */
16+
create: (args: CreateAliasArgs) => Promise<void>;
17+
18+
/**
19+
* List all aliases defined in the schema.
20+
*
21+
* @param {string | undefined} [opts.collection] Get all aliases defined for this collection.
22+
* @returns {Promise<Alias[] | undefined>} An array of aliases.
23+
*/
24+
listAll: (opts?: AliasListAllOptions) => Promise<Alias[] | undefined>;
25+
26+
/**
27+
* Get information about an alias.
28+
*
29+
* @param {string} alias Alias to fetch.
30+
* @return {Promise<Alias>} Alias definition.
31+
*/
32+
get: (alias: string) => Promise<Alias>;
33+
34+
/**
35+
* Replace target collection the alias points to.
36+
*
37+
* To change the alias that points to the collection,
38+
* delete the alias and create a new one.
39+
*
40+
* @param {string} args.alias Alias to update.
41+
* @param {string} args.collection New collection the alias should point to.
42+
* @return {Promise<void>} Awaitable promise.
43+
*/
44+
update: (args: UpdateAliasArgs) => Promise<void>;
45+
46+
/**
47+
* Delete a collection alias.
48+
*
49+
* @param {string} alias Alias definition to delete.
50+
* @return {Promise<void>} Awaitable promise.
51+
*/
52+
delete: (alias: string) => Promise<void>;
53+
}
54+
55+
const alias = (connection: ConnectionREST): Aliases => {
56+
return {
57+
create: (args: CreateAliasArgs) =>
58+
connection.postReturn<WeaviateAlias, void>(`/aliases/`, { ...args, class: args.collection }),
59+
listAll: (opts?: AliasListAllOptions) =>
60+
connection
61+
.get<WeaviateAliasResponse>(
62+
`/aliases${opts?.collection !== undefined ? '/?class=' + opts.collection : ''}`
63+
)
64+
.then((aliases) =>
65+
aliases.aliases !== undefined
66+
? aliases.aliases.map((alias) => ({ alias: alias.alias, collection: alias.class }))
67+
: []
68+
),
69+
get: (alias: string) =>
70+
connection
71+
.get<WeaviateAlias>(`/aliases/${alias}`)
72+
.then((alias) => ({ alias: alias.alias!, collection: alias.class! })),
73+
update: (args: UpdateAliasArgs) =>
74+
connection.put(`/aliases/${args.alias}`, { class: args.newTargetCollection }),
75+
delete: (alias: string) => connection.delete(`/aliases/${alias}`, null),
76+
};
77+
};
78+
79+
export default alias;

src/alias/journey.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import weaviate, { WeaviateClient } from '..';
2+
import { requireAtLeast } from '../../test/version';
3+
import { Alias } from './types';
4+
5+
requireAtLeast(1, 32, 0).describe('manages collection aliases', () => {
6+
let client: WeaviateClient;
7+
const collectionsWithAliases = ['PaulHewson', 'GeorgeBarnes', 'ColsonBaker'];
8+
9+
beforeAll(async () => {
10+
client = await weaviate.connectToLocal();
11+
await Promise.all(collectionsWithAliases.map(client.collections.delete));
12+
await Promise.all(collectionsWithAliases.map((name) => client.collections.create({ name })));
13+
});
14+
15+
it('should create alias', () => {
16+
return Promise.all([
17+
client.alias.create({ collection: 'PaulHewson', alias: 'Bono' }),
18+
client.alias.create({ collection: 'GeorgeBarnes', alias: 'MachineGunKelly' }),
19+
])
20+
.then(() => client.alias.listAll())
21+
.then((aliases) => {
22+
expect(aliases).not.toBeUndefined();
23+
expect(aliases).toHaveLength(2);
24+
expect(aliases).toEqual<Alias[]>([
25+
{ collection: 'PaulHewson', alias: 'Bono' },
26+
{ collection: 'GeorgeBarnes', alias: 'MachineGunKelly' },
27+
]);
28+
});
29+
});
30+
31+
it('should update alias', () => {
32+
return client.alias
33+
.update({ alias: 'MachineGunKelly', newTargetCollection: 'ColsonBaker' })
34+
.then(() => client.alias.get('MachineGunKelly'))
35+
.then((alias) => {
36+
expect(alias.collection).toEqual('ColsonBaker');
37+
});
38+
});
39+
40+
it('should delete alias Bono', () => {
41+
return client.alias
42+
.delete('Bono')
43+
.then(() => client.alias.listAll({ collection: 'PaulHewson' }))
44+
.then((aliases) => expect(aliases).toEqual([]));
45+
});
46+
47+
it('should delete alias MachineGunKelly', () => {
48+
return client.alias
49+
.delete('MachineGunKelly')
50+
.then(() => client.alias.listAll({ collection: 'ColsonBaker' }))
51+
.then((aliases) => expect(aliases).toEqual([]));
52+
});
53+
});

src/alias/types.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export type Alias = {
2+
collection: string;
3+
alias: string;
4+
};
5+
6+
export type CreateAliasArgs = {
7+
collection: string;
8+
alias: string;
9+
};
10+
11+
export type UpdateAliasArgs = {
12+
newTargetCollection: string;
13+
alias: string;
14+
};
15+
16+
export type AliasListAllOptions = {
17+
collection?: string | undefined;
18+
};

src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import { LiveChecker, OpenidConfigurationGetter, ReadyChecker } from './misc/ind
3838

3939
import weaviateV2 from './v2/index.js';
4040

41+
import alias, { Aliases } from './alias/index.js';
4142
import filter from './collections/filters/index.js';
4243
import { ConsistencyLevel } from './data/replication.js';
4344
import users, { Users } from './users/index.js';
@@ -102,6 +103,7 @@ export type ClientParams = {
102103
};
103104

104105
export interface WeaviateClient {
106+
alias: Aliases;
105107
backup: Backup;
106108
cluster: Cluster;
107109
collections: Collections;
@@ -224,6 +226,7 @@ async function client(params: ClientParams): Promise<WeaviateClient> {
224226
});
225227

226228
const ifc: WeaviateClient = {
229+
alias: alias(connection),
227230
backup: backup(connection),
228231
cluster: cluster(connection),
229232
collections: collections(connection, dbVersionSupport),

src/openapi/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,8 @@ export type WeaviateUserType = definitions['UserTypeOutput'];
7474
export type WeaviateUserTypeInternal = definitions['UserTypeInput'];
7575
export type WeaviateUserTypeDB = definitions['DBUserInfo']['dbUserType'];
7676
export type WeaviateAssignedUser = operations['getUsersForRole']['responses']['200']['schema'][0];
77+
// Alias
78+
export type WeaviateAlias = definitions['Alias'];
79+
export type WeaviateAliasResponse = {
80+
aliases?: Required<Exclude<definitions['AliasResponse']['aliases'], undefined>[0]>[] | undefined;
81+
};

src/roles/index.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
Role as WeaviateRole,
66
} from '../openapi/types.js';
77
import {
8+
AliasPermission,
89
BackupsPermission,
910
ClusterPermission,
1011
CollectionsPermission,
@@ -150,6 +151,33 @@ const roles = (connection: ConnectionREST): Roles => {
150151
};
151152

152153
export const permissions = {
154+
/**
155+
* Create a set of permissions specific to Weaviate's collection aliasing functionality.
156+
*
157+
* @param {string | string[]} [args.alias] Aliases that will be associated with these permissions.
158+
* @returns {AliasPermission[]} The permissions for the specified aliases.
159+
*/
160+
aliases: (args: {
161+
alias: string | string[];
162+
collection: string | string[];
163+
create?: boolean;
164+
read?: boolean;
165+
update?: boolean;
166+
delete?: boolean;
167+
}): AliasPermission[] => {
168+
const aliases = Array.isArray(args.alias) ? args.alias : [args.alias];
169+
const collections = Array.isArray(args.collection) ? args.collection : [args.collection];
170+
const combinations = aliases.flatMap((alias) => collections.map((collection) => ({ alias, collection })));
171+
return combinations.map(({ collection, alias }) => {
172+
const out: AliasPermission = { alias, collection, actions: [] };
173+
if (args.create) out.actions.push('create_aliases');
174+
if (args.read) out.actions.push('read_aliases');
175+
if (args.update) out.actions.push('update_aliases');
176+
if (args.delete) out.actions.push('delete_aliases');
177+
return out;
178+
});
179+
},
180+
153181
/**
154182
* Create a set of permissions specific to Weaviate's backup functionality.
155183
*

src/roles/integration.test.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ type TestCase = {
1616
roleName: string;
1717
permissions: Permission[];
1818
expected: Role;
19+
requireVersion?: [number, number, number];
1920
};
2021

2122
const emptyPermissions = {
23+
aliasPermissions: [],
2224
backupsPermissions: [],
2325
clusterPermissions: [],
2426
collectionsPermissions: [],
@@ -277,6 +279,23 @@ const testCases: TestCase[] = [
277279
usersPermissions: [{ users: 'some-user', actions: ['assign_and_revoke_users', 'read_users'] }],
278280
},
279281
},
282+
{
283+
roleName: 'aliases',
284+
requireVersion: [1, 32, 0],
285+
permissions: weaviate.permissions.aliases({
286+
alias: 'SomeAlias',
287+
collection: 'SomeCollection',
288+
create: true,
289+
delete: true,
290+
}),
291+
expected: {
292+
name: 'aliases',
293+
...emptyPermissions,
294+
aliasPermissions: [
295+
{ alias: 'SomeAlias', collection: 'SomeCollection', actions: ['create_aliases', 'delete_aliases'] },
296+
],
297+
},
298+
},
280299
];
281300

282301
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',
345364

346365
describe('should be able to create roles using the permissions factory', () => {
347366
testCases.forEach((testCase) => {
348-
it(`with ${testCase.roleName} permissions`, async () => {
349-
await client.roles.create(testCase.roleName, testCase.permissions);
350-
const role = await client.roles.byName(testCase.roleName);
351-
expect(role).toEqual(testCase.expected);
352-
});
367+
(testCase.requireVersion !== undefined ? requireAtLeast(...testCase.requireVersion).it : it)(
368+
`with ${testCase.roleName} permissions`,
369+
async () => {
370+
await client.roles.create(testCase.roleName, testCase.permissions);
371+
const role = await client.roles.byName(testCase.roleName);
372+
expect(role).toEqual(testCase.expected);
373+
}
374+
);
353375
});
354376
});
355377

src/roles/types.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { Action, WeaviateUserType } from '../openapi/types.js';
22

3+
export type AliasAction = Extract<
4+
Action,
5+
'create_aliases' | 'read_aliases' | 'update_aliases' | 'delete_aliases'
6+
>;
37
export type BackupsAction = Extract<Action, 'manage_backups'>;
48
export type ClusterAction = Extract<Action, 'read_cluster'>;
59
export type CollectionsAction = Extract<
@@ -27,6 +31,12 @@ export type UserAssignment = {
2731
userType: WeaviateUserType;
2832
};
2933

34+
export type AliasPermission = {
35+
alias: string;
36+
collection: string;
37+
actions: AliasAction[];
38+
};
39+
3040
export type BackupsPermission = {
3141
collection: string;
3242
actions: BackupsAction[];
@@ -71,6 +81,7 @@ export type UsersPermission = {
7181

7282
export type Role = {
7383
name: string;
84+
aliasPermissions: AliasPermission[];
7485
backupsPermissions: BackupsPermission[];
7586
clusterPermissions: ClusterPermission[];
7687
collectionsPermissions: CollectionsPermission[];
@@ -82,6 +93,7 @@ export type Role = {
8293
};
8394

8495
export type Permission =
96+
| AliasPermission
8597
| BackupsPermission
8698
| ClusterPermission
8799
| CollectionsPermission

0 commit comments

Comments
 (0)