diff --git a/docs/api/cozy-client/modules/models.sharing.md b/docs/api/cozy-client/modules/models.sharing.md
index 22bf792f2..24012e493 100644
--- a/docs/api/cozy-client/modules/models.sharing.md
+++ b/docs/api/cozy-client/modules/models.sharing.md
@@ -6,9 +6,9 @@
## Functions
-### getSharingLink
+### makeSharingLink
-▸ **getSharingLink**(`client`, `filesIds`, `options?`): `Promise`<`string`>
+▸ **makeSharingLink**(`client`, `filesIds`, `options?`): `Promise`<`string`>
Generate Sharing link for one or many files
@@ -21,6 +21,7 @@ Generate Sharing link for one or many files
| `options` | `Object` | Options |
| `options.password` | `string` | - |
| `options.ttl` | `string` | - |
+| `options.verbs` | `string`\[] | - |
*Returns*
@@ -30,4 +31,4 @@ Shared link
*Defined in*
-[packages/cozy-client/src/models/sharing.js:15](https://github.com/cozy/cozy-client/blob/master/packages/cozy-client/src/models/sharing.js#L15)
+[packages/cozy-client/src/models/sharing.js:16](https://github.com/cozy/cozy-client/blob/master/packages/cozy-client/src/models/sharing.js#L16)
diff --git a/docs/api/cozy-stack-client.md b/docs/api/cozy-stack-client.md
index 83f78be9f..548f41d93 100644
--- a/docs/api/cozy-stack-client.md
+++ b/docs/api/cozy-stack-client.md
@@ -1667,7 +1667,7 @@ Implements `DocumentCollection` API along with specific methods for `io.cozy.per
* [PermissionCollection](#PermissionCollection)
* [.create(permission)](#PermissionCollection+create)
- * [.add(document, permission)](#PermissionCollection+add) ⇒ Promise
+ * [.add(document, permission, options)](#PermissionCollection+add) ⇒ Promise
* ~~[.findApps()](#PermissionCollection+findApps)~~
* [.createSharingLink(document, options)](#PermissionCollection+createSharingLink)
* [.fetchPermissionsByLink(permissions)](#PermissionCollection+fetchPermissionsByLink)
@@ -1692,7 +1692,7 @@ It can also associates one or more codes to it, via the codes parameter
-### permissionCollection.add(document, permission) ⇒ Promise
+### permissionCollection.add(document, permission, options) ⇒ Promise
Adds a permission to the given document. Document type must be
`io.cozy.apps`, `io.cozy.konnectors` or `io.cozy.permissions`
@@ -1702,18 +1702,23 @@ Adds a permission to the given document. Document type must be
| --- | --- | --- |
| document | object
| Document which receives the permission |
| permission | object
| Describes the permission |
+| options | object
| options |
+| [options.expiresAt] | string
| Date at which the permission will expire. Set to "" to remove it. |
+| [options.password] | string
| To generate a password-protected link. Set to "" to remove it. |
**Example**
```
-const permissions = await client
- .collection('io.cozy.permissions')
- .add(konnector, {
+const permissions = await client.collection('io.cozy.permissions').add(
+ konnector,
+ {
folder: {
type: 'io.cozy.files',
verbs: ['GET', 'PUT'],
values: [`io.cozy.files.bc57b60eb2954537b0dcdc6ebd8e9d23`]
}
- })
+ },
+ { expiresAt: '2100-01-01T00:00:00Z', password: 'password' }
+)
```
@@ -1732,7 +1737,11 @@ Create a share link
| --- | --- | --- |
| document | Object
| cozy document |
| options | object
| options |
-| options.verbs | Array.<string>
| explicit permissions to use |
+| [options.ttl] | string
| Time to live (bigduration format, e.g. "4Y3M2D1h30m15s") |
+| [options.password] | string
| To generate a password-protected link |
+| [options.verbs] | Array.<string>
| explicit permissions to use |
+| [options.codes] | string
| A comma separed list of values (defaulted to code) |
+| [options.tiny] | boolean
| If set to true then the generated shortcode will be 6 digits Cozy-Stack has a few conditions to be able to use this tiny shortcode ATM you have to specifiy a ttl < 1h, but it can change. see https://docs.cozy.io/en/cozy-stack/permissions/#post-permissions for exact informations |
@@ -2305,7 +2314,7 @@ Build a permission set
| document | Object
| cozy document |
| publicLink | boolean
| are the permissions for a public link ? |
| options | object
| options |
-| options.verbs | Array.<string>
| explicit permissions to use |
+| [options.verbs] | Array.<string>
| explicit permissions to use |
diff --git a/packages/cozy-client/src/models/sharing.js b/packages/cozy-client/src/models/sharing.js
index a1ff58d92..6f4e05117 100644
--- a/packages/cozy-client/src/models/sharing.js
+++ b/packages/cozy-client/src/models/sharing.js
@@ -10,17 +10,18 @@ import { generateWebLink } from '../helpers'
* @param {object} options - Options
* @param {string} [options.ttl] - Time to live (bigduration format, e.g. "4Y3M2D1h30m15s")
* @param {string} [options.password] - To generate a password-protected link
+ * @param {string[]} [options.verbs] - Array of verbs to allow on the shared link. Default is ['GET']
* @returns {Promise} Shared link
*/
-export const getSharingLink = async (
+export const makeSharingLink = async (
client,
filesIds,
- { ttl, password } = {}
+ { ttl, password, verbs = ['GET'] } = {}
) => {
const PERMS = {
_type: DOCTYPE_PERMISSIONS,
permissions: {
- files: { type: DOCTYPE_FILES, values: filesIds, verbs: ['GET'] }
+ files: { type: DOCTYPE_FILES, values: filesIds, verbs }
},
...(ttl && { ttl }),
...(password && { password })
diff --git a/packages/cozy-client/src/models/sharing.spec.js b/packages/cozy-client/src/models/sharing.spec.js
index 37d4d76b3..d02738b93 100644
--- a/packages/cozy-client/src/models/sharing.spec.js
+++ b/packages/cozy-client/src/models/sharing.spec.js
@@ -1,6 +1,6 @@
-import { getSharingLink } from './sharing'
+import { makeSharingLink } from './sharing'
-describe('getSharingLink', () => {
+describe('makeSharingLink', () => {
const mockSharecode = { attributes: { shortcodes: { code: 'shortcode' } } }
const mockClient = ({ isFlatDomain = false } = {}) => ({
save: jest.fn(() => ({ data: mockSharecode })),
@@ -10,7 +10,7 @@ describe('getSharingLink', () => {
const mockFiles = ['fileId01', 'fileId02']
it('should generate the right share link if "isFlatDomain" param is not defined', async () => {
- const sharingLink = await getSharingLink(mockClient(), mockFiles)
+ const sharingLink = await makeSharingLink(mockClient(), mockFiles)
expect(sharingLink).toBe(
'http://drive.cozy.cloud/public?sharecode=shortcode#/'
@@ -18,7 +18,7 @@ describe('getSharingLink', () => {
})
it('should generate the right share link to a nested cozy', async () => {
- const sharingLink = await getSharingLink(
+ const sharingLink = await makeSharingLink(
mockClient({ isFlatDomain: false }),
mockFiles
)
@@ -29,7 +29,7 @@ describe('getSharingLink', () => {
})
it('should generate the right share link to a flat cozy', async () => {
- const sharingLink = await getSharingLink(
+ const sharingLink = await makeSharingLink(
mockClient({ isFlatDomain: true }),
mockFiles
)
@@ -40,14 +40,14 @@ describe('getSharingLink', () => {
})
it('should generate the right share link with an correct sharecode', async () => {
- const sharingLink = await getSharingLink(mockClient(), mockFiles)
+ const sharingLink = await makeSharingLink(mockClient(), mockFiles)
expect(sharingLink).toContain('sharecode=shortcode')
})
it('should save called with the "ttl" param', async () => {
const mockSave = jest.fn().mockReturnValue({ data: mockSharecode })
const client = { ...mockClient(), save: mockSave }
- await getSharingLink(client, mockFiles, {
+ await makeSharingLink(client, mockFiles, {
ttl: '1d'
})
@@ -67,7 +67,7 @@ describe('getSharingLink', () => {
it('should save called with the "password" param', async () => {
const mockSave = jest.fn().mockReturnValue({ data: mockSharecode })
const client = { ...mockClient(), save: mockSave }
- await getSharingLink(client, mockFiles, {
+ await makeSharingLink(client, mockFiles, {
password: 'password'
})
@@ -84,10 +84,29 @@ describe('getSharingLink', () => {
})
})
+ it('should save called with the "verbs" param', async () => {
+ const mockSave = jest.fn().mockReturnValue({ data: mockSharecode })
+ const client = { ...mockClient(), save: mockSave }
+ await makeSharingLink(client, mockFiles, {
+ verbs: ['GET', 'POST']
+ })
+
+ expect(mockSave).toBeCalledWith({
+ _type: 'io.cozy.permissions',
+ permissions: {
+ files: {
+ type: 'io.cozy.files',
+ values: ['fileId01', 'fileId02'],
+ verbs: ['GET', 'POST']
+ }
+ }
+ })
+ })
+
it('should save called without the "ttl" or "password" params', async () => {
const mockSave = jest.fn().mockReturnValue({ data: mockSharecode })
const client = { ...mockClient(), save: mockSave }
- await getSharingLink(client, mockFiles)
+ await makeSharingLink(client, mockFiles)
expect(mockSave).toBeCalledWith({
_type: 'io.cozy.permissions',
diff --git a/packages/cozy-client/types/models/sharing.d.ts b/packages/cozy-client/types/models/sharing.d.ts
index 111ea8d03..0a5621b4a 100644
--- a/packages/cozy-client/types/models/sharing.d.ts
+++ b/packages/cozy-client/types/models/sharing.d.ts
@@ -1,5 +1,6 @@
-export function getSharingLink(client: CozyClient, filesIds: string[], { ttl, password }?: {
+export function makeSharingLink(client: CozyClient, filesIds: string[], { ttl, password, verbs }?: {
ttl: string;
password: string;
+ verbs: string[];
}): Promise;
import CozyClient from "../CozyClient";
diff --git a/packages/cozy-stack-client/src/PermissionCollection.js b/packages/cozy-stack-client/src/PermissionCollection.js
index 7b966fb04..73a4c5b09 100644
--- a/packages/cozy-stack-client/src/PermissionCollection.js
+++ b/packages/cozy-stack-client/src/PermissionCollection.js
@@ -61,22 +61,27 @@ class PermissionCollection extends DocumentCollection {
*
* @param {object} document - Document which receives the permission
* @param {object} permission - Describes the permission
+ * @param {object} options - options
+ * @param {string} [options.expiresAt] - Date at which the permission will expire. Set to "" to remove it.
+ * @param {string} [options.password] - To generate a password-protected link. Set to "" to remove it.
* @returns {Promise}
*
* @example
* ```
- * const permissions = await client
- * .collection('io.cozy.permissions')
- * .add(konnector, {
+ * const permissions = await client.collection('io.cozy.permissions').add(
+ * konnector,
+ * {
* folder: {
* type: 'io.cozy.files',
* verbs: ['GET', 'PUT'],
* values: [`io.cozy.files.bc57b60eb2954537b0dcdc6ebd8e9d23`]
* }
- * })
+ * },
+ * { expiresAt: '2100-01-01T00:00:00Z', password: 'password' }
+ * )
* ```
*/
- async add(document, permission) {
+ async add(document, permission, options = {}) {
let endpoint
switch (document._type) {
case 'io.cozy.apps':
@@ -94,11 +99,19 @@ class PermissionCollection extends DocumentCollection {
)
}
+ const { expiresAt, password } = options
+ // We need to pass password and expires_at even if they are empty strings because the API expects them.
+ // If value is a empty string, the stack will remove the password or expires_at.
+ const hasPassword = password || password === ''
+ const hasExpiresAt = expiresAt || expiresAt === ''
+
const resp = await this.stackClient.fetchJSON('PATCH', endpoint, {
data: {
type: 'io.cozy.permissions',
attributes: {
- permissions: permission
+ permissions: permission,
+ ...(hasPassword && { password }),
+ ...(hasExpiresAt && { expires_at: expiresAt })
}
}
})
@@ -143,13 +156,25 @@ class PermissionCollection extends DocumentCollection {
*
* @param {{_id, _type}} document - cozy document
* @param {object} options - options
- * @param {string[]} options.verbs - explicit permissions to use
+ * @param {string} [options.ttl] - Time to live (bigduration format, e.g. "4Y3M2D1h30m15s")
+ * @param {string} [options.password] - To generate a password-protected link
+ * @param {string[]} [options.verbs] - explicit permissions to use
+ * @param {string} [options.codes] A comma separed list of values (defaulted to code)
+ * @param {boolean} [options.tiny] If set to true then the generated shortcode will be 6 digits
+ * Cozy-Stack has a few conditions to be able to use this tiny shortcode ATM you have to specifiy
+ * a ttl < 1h, but it can change.
+ * see https://docs.cozy.io/en/cozy-stack/permissions/#post-permissions for exact informations
*/
async createSharingLink(document, options = {}) {
- const { verbs } = options
+ const { ttl, password, verbs, tiny, codes = 'code' } = options
+ const searchParams = new URLSearchParams()
+ searchParams.append('codes', codes)
+ if (ttl) searchParams.append('ttl', ttl)
+ if (tiny) searchParams.append('tiny', true)
+
const resp = await this.stackClient.fetchJSON(
'POST',
- `/permissions?codes=email`,
+ `/permissions?${searchParams}`,
{
data: {
type: 'io.cozy.permissions',
@@ -158,7 +183,8 @@ class PermissionCollection extends DocumentCollection {
document,
true,
verbs ? { verbs } : {}
- )
+ ),
+ ...(password && { password })
}
}
}
@@ -245,7 +271,7 @@ class PermissionCollection extends DocumentCollection {
* @param {{_id, _type}} document - cozy document
* @param {boolean} publicLink - are the permissions for a public link ?
* @param {object} options - options
- * @param {string[]} options.verbs - explicit permissions to use
+ * @param {string[]} [options.verbs] - explicit permissions to use
* @returns {object} permissions object that can be sent through /permissions/*
*/
export const getPermissionsFor = (
diff --git a/packages/cozy-stack-client/src/PermissionCollection.spec.js b/packages/cozy-stack-client/src/PermissionCollection.spec.js
index a10390892..8c773ca68 100644
--- a/packages/cozy-stack-client/src/PermissionCollection.spec.js
+++ b/packages/cozy-stack-client/src/PermissionCollection.spec.js
@@ -99,6 +99,81 @@ describe('PermissionCollection', () => {
)
})
+ it('should call with options if they are defined and filled in', async () => {
+ await collection.add(
+ {
+ _type: 'io.cozy.permissions',
+ _id: 'a340d5e0d64711e6b66c5fc9ce1e17c6'
+ },
+ fixtures.permission,
+ { expiresAt: '2019-01-01T00:00:00Z', password: 'password' }
+ )
+
+ expect(client.fetchJSON).toHaveBeenCalledWith(
+ 'PATCH',
+ '/permissions/a340d5e0d64711e6b66c5fc9ce1e17c6',
+ {
+ data: {
+ type: 'io.cozy.permissions',
+ attributes: {
+ permissions: fixtures.permission,
+ expires_at: '2019-01-01T00:00:00Z',
+ password: 'password'
+ }
+ }
+ }
+ )
+ })
+
+ it('should call with options if they are defined but not filled in', async () => {
+ await collection.add(
+ {
+ _type: 'io.cozy.permissions',
+ _id: 'a340d5e0d64711e6b66c5fc9ce1e17c6'
+ },
+ fixtures.permission,
+ { expiresAt: '', password: '' }
+ )
+
+ expect(client.fetchJSON).toHaveBeenCalledWith(
+ 'PATCH',
+ '/permissions/a340d5e0d64711e6b66c5fc9ce1e17c6',
+ {
+ data: {
+ type: 'io.cozy.permissions',
+ attributes: {
+ permissions: fixtures.permission,
+ expires_at: '',
+ password: ''
+ }
+ }
+ }
+ )
+ })
+
+ it('should call without options if they are undefined', async () => {
+ await collection.add(
+ {
+ _type: 'io.cozy.permissions',
+ _id: 'a340d5e0d64711e6b66c5fc9ce1e17c6'
+ },
+ fixtures.permission
+ )
+
+ expect(client.fetchJSON).toHaveBeenCalledWith(
+ 'PATCH',
+ '/permissions/a340d5e0d64711e6b66c5fc9ce1e17c6',
+ {
+ data: {
+ type: 'io.cozy.permissions',
+ attributes: {
+ permissions: fixtures.permission
+ }
+ }
+ }
+ )
+ })
+
it('uses expected apps endpoint', async () => {
await collection.add(
{
@@ -186,7 +261,7 @@ describe('PermissionCollection', () => {
await collection.createSharingLink(document)
expect(client.fetchJSON).toHaveBeenCalledWith(
'POST',
- '/permissions?codes=email',
+ '/permissions?codes=code',
{
data: {
type: 'io.cozy.permissions',
@@ -211,7 +286,7 @@ describe('PermissionCollection', () => {
await collection.createSharingLink(document, options)
expect(client.fetchJSON).toHaveBeenCalledWith(
'POST',
- '/permissions?codes=email',
+ '/permissions?codes=code',
{
data: {
type: 'io.cozy.permissions',
@@ -228,6 +303,20 @@ describe('PermissionCollection', () => {
}
)
})
+
+ it('Should be call with the right params', async () => {
+ const document = { _type: 'io.cozy.files', _id: '1234' }
+ await collection.createSharingLink(document, {
+ ttl: '1D',
+ tiny: true,
+ codes: 'a,b'
+ })
+ expect(client.fetchJSON).toHaveBeenCalledWith(
+ 'POST',
+ '/permissions?codes=a%2Cb&ttl=1D&tiny=true',
+ { data: { attributes: {}, type: 'io.cozy.permissions' } }
+ )
+ })
})
})