From 5e8799720be99078c14e1ba994c85bc67ee2ea0e Mon Sep 17 00:00:00 2001 From: doubleface Date: Mon, 16 Mar 2026 11:54:09 +0100 Subject: [PATCH] feat: Use the right endpoint when creating shared drives The POST on `/sharings/drives` has more checks than POST on `/sharings` with `drive: true` option --- docs/api/cozy-stack-client.md | 16 ++++ .../src/SharingCollection.js | 48 ++++++++++- .../src/SharingCollection.spec.js | 80 +++++++++++++++++++ 3 files changed, 141 insertions(+), 3 deletions(-) diff --git a/docs/api/cozy-stack-client.md b/docs/api/cozy-stack-client.md index 0c560e42d..64d634e98 100644 --- a/docs/api/cozy-stack-client.md +++ b/docs/api/cozy-stack-client.md @@ -1982,6 +1982,7 @@ Implements the `DocumentCollection` API along with specific methods for * [.renameSharedDrive(sharing, newName)](#SharingCollection+renameSharedDrive) ⇒ object * [.fetchSharedDrives()](#SharingCollection+fetchSharedDrives) ⇒ Promise.<{data: Array.<Sharing>}> * [.create(params)](#SharingCollection+create) + * [.createSharedDrive(params)](#SharingCollection+createSharedDrive) * [.getDiscoveryLink(sharingId, sharecode, [options])](#SharingCollection+getDiscoveryLink) ⇒ string * [.addRecipients(options)](#SharingCollection+addRecipients) * [.revokeRecipient(sharing, recipientIndex)](#SharingCollection+revokeRecipient) @@ -2055,6 +2056,21 @@ Creates a new Sharing. See https://docs.cozy.io/en/cozy-stack/sharing/#post-shar | [params.appSlug] | string | Slug of the targeted app | | [params.sharedDrive] | boolean | If the sharing is a shared drive | + + +### sharingCollection.createSharedDrive(params) +Creates a new shared drive. See https://docs.cozy.io/en/cozy-stack/shared-drives/ + +**Kind**: instance method of [SharingCollection](#SharingCollection) + +| Param | Type | Description | +| --- | --- | --- | +| params | object | Sharing params | +| params.document | [Sharing](#Sharing) | The document to share. Should have an _id | +| params.description | string | Description of the sharing | +| [params.recipients] | [Array.<Recipient>](#Recipient) | Recipients to add to the sharing | +| [params.readOnlyRecipients] | [Array.<Recipient>](#Recipient) | Recipients to add with read only access | + ### sharingCollection.getDiscoveryLink(sharingId, sharecode, [options]) ⇒ string diff --git a/packages/cozy-stack-client/src/SharingCollection.js b/packages/cozy-stack-client/src/SharingCollection.js index 8842c5167..b88d3eabd 100644 --- a/packages/cozy-stack-client/src/SharingCollection.js +++ b/packages/cozy-stack-client/src/SharingCollection.js @@ -150,6 +150,15 @@ class SharingCollection extends DocumentCollection { appSlug, sharedDrive }) { + if (sharedDrive) { + return this.createSharedDrive({ + document, + description, + recipients, + readOnlyRecipients + }) + } + const attributes = { description, preview_path: previewPath, @@ -160,9 +169,6 @@ class SharingCollection extends DocumentCollection { if (appSlug) { optionalAttributes.app_slug = appSlug } - if (sharedDrive) { - optionalAttributes.drive = sharedDrive - } const resp = await this.stackClient.fetchJSON('POST', '/sharings/', { data: { @@ -183,6 +189,42 @@ class SharingCollection extends DocumentCollection { return { data: normalizeSharing(resp.data) } } + /** + * Creates a new shared drive. See https://docs.cozy.io/en/cozy-stack/shared-drives/ + * + * @param {object} params Sharing params + * @param {Sharing} params.document The document to share. Should have an _id + * @param {string} params.description Description of the sharing + * @param {Array=} params.recipients Recipients to add to the sharing + * @param {Array=} params.readOnlyRecipients Recipients to add with read only access + */ + async createSharedDrive({ + document, + description, + recipients = [], + readOnlyRecipients = [] + }) { + const resp = await this.stackClient.fetchJSON('POST', '/sharings/drives', { + data: { + attributes: { + folder_id: document._id, + description + }, + relationships: { + ...(recipients.length > 0 && { + recipients: { data: recipients.map(toRelationshipItem) } + }), + ...(readOnlyRecipients.length > 0 && { + read_only_recipients: { + data: readOnlyRecipients.map(toRelationshipItem) + } + }) + } + } + }) + return { data: normalizeSharing(resp.data) } + } + /** * getDiscoveryLink - Returns the URL of the page that can be used to accept a sharing. See https://docs.cozy.io/en/cozy-stack/sharing/#get-sharingssharing-iddiscovery * diff --git a/packages/cozy-stack-client/src/SharingCollection.spec.js b/packages/cozy-stack-client/src/SharingCollection.spec.js index 25e8a9a69..9a9171743 100644 --- a/packages/cozy-stack-client/src/SharingCollection.spec.js +++ b/packages/cozy-stack-client/src/SharingCollection.spec.js @@ -455,6 +455,86 @@ describe('SharingCollection', () => { }) }) + describe('createSharedDrive', () => { + beforeEach(() => { + client.fetch.mockReset() + client.fetchJSON.mockResolvedValue({ data: [] }) + }) + + it('should call POST /sharings/drives with folder_id and description', async () => { + await collection.createSharedDrive({ + document: FOLDER, + recipients: [RECIPIENT], + description: 'shared drive' + }) + + expect(client.fetchJSON).toHaveBeenCalledWith( + 'POST', + '/sharings/drives', + { + data: { + attributes: { + folder_id: FOLDER._id, + description: 'shared drive' + }, + relationships: { + recipients: { + data: [{ id: 'contact_1', type: 'io.cozy.contacts' }] + } + } + } + } + ) + }) + + it('should support read only recipients', async () => { + await collection.createSharedDrive({ + document: FOLDER, + readOnlyRecipients: [RECIPIENT], + description: 'test' + }) + + expect(client.fetchJSON).toHaveBeenCalledWith( + 'POST', + '/sharings/drives', + { + data: { + attributes: { + folder_id: FOLDER._id, + description: 'test' + }, + relationships: { + read_only_recipients: { + data: [{ id: 'contact_1', type: 'io.cozy.contacts' }] + } + } + } + } + ) + }) + + it('should be called when create is called with sharedDrive: true', async () => { + const spy = jest.spyOn(collection, 'createSharedDrive') + spy.mockResolvedValue({ data: [] }) + + await collection.create({ + document: FOLDER, + recipients: [RECIPIENT], + description: 'test', + sharedDrive: true + }) + + expect(spy).toHaveBeenCalledWith({ + document: FOLDER, + description: 'test', + recipients: [RECIPIENT], + readOnlyRecipients: [] + }) + + spy.mockRestore() + }) + }) + describe('revokeAllRecipients', () => { beforeEach(() => { client.fetch.mockReset()