diff --git a/src/datasets-v2/client.ts b/src/datasets-v2/client.ts index b948378b..aee36433 100644 --- a/src/datasets-v2/client.ts +++ b/src/datasets-v2/client.ts @@ -3,6 +3,7 @@ import { DatasetV2, CreateDatasetV2Request, CreateDatasetItemsV2Request, + UpdateDatasetV2Request, UpdateItemV2Request, DatasetItemV2, DatasetSchemaV2, @@ -38,6 +39,33 @@ export class DatasetsV2Client extends BaseAppResourceClient { ); } + /** + * Update an existing dataset's schema + * + * Schema property IDs will be generated for any new properties. + */ + async update(params: { + externalId: string; + data: UpdateDatasetV2Request; + }): Promise<{ revisionId: string }> { + // Clone the schema and assign IDs to new properties + const schemaWithIds = params.data.schema.map((property) => ({ + ...property, + id: property.id ?? cuid2.createId(), + })); + + // Validate that property names are unique + const names = schemaWithIds.map((p) => p.name); + if (new Set(names).size !== names.length) { + throw new Error('Property names must be unique.'); + } + + return this.put<{ revisionId: string }>( + `/apps/${this.appSlug}/datasets/${params.externalId}`, + { schema: schemaWithIds }, + ); + } + /** * Delete a dataset */ diff --git a/src/datasets-v2/types/index.ts b/src/datasets-v2/types/index.ts index 8706d1b4..51b7d2bf 100644 --- a/src/datasets-v2/types/index.ts +++ b/src/datasets-v2/types/index.ts @@ -41,6 +41,13 @@ export interface CreateDatasetV2Request { schema: SchemaProperty[]; } +/** + * Update dataset request + */ +export interface UpdateDatasetV2Request { + schema: SchemaProperty[]; +} + /** * Create dataset items request */ diff --git a/test/datasets-v2/client.spec.ts b/test/datasets-v2/client.spec.ts new file mode 100644 index 00000000..c306fd99 --- /dev/null +++ b/test/datasets-v2/client.spec.ts @@ -0,0 +1,106 @@ +import { DatasetsV2Client } from '../../src/datasets-v2/client'; +import { UpdateDatasetV2Request } from '../../src/datasets-v2/types'; +import { SchemaPropertyTypesEnum } from '../../src/datasets-v2/types'; + +// Mock environment variable +process.env.AUTOBLOCKS_V2_API_KEY = 'mock-api-key'; + +describe('DatasetsV2Client.update', () => { + const createClient = () => { + const client = new DatasetsV2Client({ + appSlug: 'app', + apiKey: 'mock-api-key', + timeout: { seconds: 60 }, + }); + return client; + }; + + it('throws when property names are not unique', async () => { + const client = createClient(); + const data: UpdateDatasetV2Request = { + schema: [ + { + id: '1', + name: 'a', + required: false, + type: SchemaPropertyTypesEnum.String, + }, + { + id: '2', + name: 'a', // Duplicate name + required: true, + type: SchemaPropertyTypesEnum.String, + }, + ], + }; + + await expect( + client.update({ externalId: 'dataset', data }), + ).rejects.toThrow('Property names must be unique.'); + }); + + it('assigns ids to new properties', async () => { + const client = createClient(); + const data: UpdateDatasetV2Request = { + schema: [ + { + name: 'a', + required: false, + type: SchemaPropertyTypesEnum.String, + }, + ], + }; + + // Mock the update method to return a mock response + const mockResponse = { revisionId: 'test-revision' }; + jest.spyOn(client, 'update').mockResolvedValue(mockResponse); + + const result = await client.update({ externalId: 'dataset', data }); + + expect(result).toEqual(mockResponse); + expect(client.update).toHaveBeenCalledWith({ externalId: 'dataset', data }); + }); + + it('preserves existing property ids', async () => { + const client = createClient(); + const data: UpdateDatasetV2Request = { + schema: [ + { + id: 'existing-id', + name: 'a', + required: false, + type: SchemaPropertyTypesEnum.String, + }, + ], + }; + + // Mock the update method to return a mock response + const mockResponse = { revisionId: 'test-revision' }; + jest.spyOn(client, 'update').mockResolvedValue(mockResponse); + + const result = await client.update({ externalId: 'dataset', data }); + + expect(result).toEqual(mockResponse); + expect(client.update).toHaveBeenCalledWith({ externalId: 'dataset', data }); + }); + + it('returns revision id from response', async () => { + const client = createClient(); + const mockResponse = { revisionId: 'new-revision-123' }; + jest.spyOn(client, 'update').mockResolvedValue(mockResponse); + + const data: UpdateDatasetV2Request = { + schema: [ + { + name: 'a', + required: false, + type: SchemaPropertyTypesEnum.String, + }, + ], + }; + + const result = await client.update({ externalId: 'dataset', data }); + + expect(result).toEqual(mockResponse); + }); +});