Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions docs/api/cozy-stack-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,9 @@ not.</p>
<dd></dd>
<dt><a href="#TwoFactorNeededRes">TwoFactorNeededRes</a></dt>
<dd></dd>
<dt><a href="#PermissionCollectionOptions">PermissionCollectionOptions</a> : <code>object</code></dt>
<dd><p>Options that can be passed to PermissionCollection&#39;s constructor</p>
</dd>
<dt><a href="#Permission">Permission</a> ⇒ <code><a href="#Permission">Permission</a></code></dt>
<dd><p>async getOwnPermissions - deprecated: please use fetchOwnPermissions instead</p>
</dd>
Expand Down Expand Up @@ -1762,6 +1765,7 @@ Implements `DocumentCollection` API along with specific methods for `io.cozy.per
**Kind**: global class

* [PermissionCollection](#PermissionCollection)
* [new PermissionCollection(doctype, stackClient, [options])](#new_PermissionCollection_new)
* [.create(permission)](#PermissionCollection+create)
* [.add(document, permission, options)](#PermissionCollection+add) ⇒ <code>Promise</code>
* ~~[.findApps()](#PermissionCollection+findApps)~~
Expand All @@ -1770,6 +1774,16 @@ Implements `DocumentCollection` API along with specific methods for `io.cozy.per
* [.fetchAllLinks(document)](#PermissionCollection+fetchAllLinks) ⇒ <code>object</code>
* [.revokeSharingLink(document)](#PermissionCollection+revokeSharingLink)

<a name="new_PermissionCollection_new"></a>

### new PermissionCollection(doctype, stackClient, [options])

| Param | Type | Description |
| --- | --- | --- |
| doctype | <code>string</code> | Doctype of the collection (should be `io.cozy.permissions`) |
| stackClient | [<code>CozyStackClient</code>](#CozyStackClient) | The client used to make requests to the server |
| [options] | [<code>PermissionCollectionOptions</code>](#PermissionCollectionOptions) | The collection options |

<a name="PermissionCollection+create"></a>

### permissionCollection.create(permission)
Expand Down Expand Up @@ -3473,6 +3487,18 @@ Options that can be passed to NotesCollection's constructor
| --- | --- | --- |
| two_factor_token | <code>string</code> | The 2FA token |

<a name="PermissionCollectionOptions"></a>

## PermissionCollectionOptions : <code>object</code>
Options that can be passed to PermissionCollection's constructor

**Kind**: global typedef
**Properties**

| Name | Type | Description |
| --- | --- | --- |
| [driveId] | <code>string</code> | ID of the shared drive targeted by the collection |

<a name="Permission"></a>

## Permission ⇒ [<code>Permission</code>](#Permission)
Expand Down
39 changes: 29 additions & 10 deletions packages/cozy-stack-client/src/PermissionCollection.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,35 @@
import DocumentCollection from './DocumentCollection'
import { normalizeDoc } from './normalize'
import { isFile } from './FileCollection'
import { uri } from './utils'
import { uri, sharedDriveApiPrefix } from './utils'
import logger from './logger'

const normalizePermission = perm => normalizeDoc(perm, 'io.cozy.permissions')

/**
* Options that can be passed to PermissionCollection's constructor
*
* @typedef {object} PermissionCollectionOptions
* @property {string} [driveId] - ID of the shared drive targeted by the collection
*/

/**
* Implements `DocumentCollection` API along with specific methods for `io.cozy.permissions`.
*/
class PermissionCollection extends DocumentCollection {
/**
* @param {string} doctype - Doctype of the collection (should be `io.cozy.permissions`)
* @param {CozyStackClient} stackClient -The client used to make requests to the server
* @param {PermissionCollectionOptions} [options] - The collection options
*/
constructor(doctype, stackClient, options = {}) {
super(doctype, stackClient, options)
this.prefix = options.driveId ? sharedDriveApiPrefix(options.driveId) : ''
}
async get(id) {
const resp = await this.stackClient.fetchJSON(
'GET',
uri`/permissions/${id}`
this.prefix + uri`/permissions/${id}`
)
return {
data: normalizePermission(resp.data)
Expand Down Expand Up @@ -42,7 +58,7 @@ class PermissionCollection extends DocumentCollection {
if (tiny) searchParams.append('tiny', true)
const resp = await this.stackClient.fetchJSON(
'POST',
`/permissions?${searchParams}`,
`${this.prefix}/permissions?${searchParams}`,
{
data: {
type: 'io.cozy.permissions',
Expand Down Expand Up @@ -85,13 +101,13 @@ class PermissionCollection extends DocumentCollection {
let endpoint
switch (document._type) {
case 'io.cozy.apps':
endpoint = `/permissions/apps/${document.slug}`
endpoint = `${this.prefix}/permissions/apps/${document.slug}`
break
case 'io.cozy.konnectors':
endpoint = `/permissions/konnectors/${document.slug}`
endpoint = `${this.prefix}/permissions/konnectors/${document.slug}`
break
case 'io.cozy.permissions':
endpoint = `/permissions/${document._id}`
endpoint = `${this.prefix}/permissions/${document._id}`
break
default:
throw new Error(
Expand Down Expand Up @@ -122,14 +138,14 @@ class PermissionCollection extends DocumentCollection {
destroy(permission) {
return this.stackClient.fetchJSON(
'DELETE',
uri`/permissions/${permission.id}`
this.prefix + uri`/permissions/${permission.id}`
)
}

async findLinksByDoctype(doctype) {
const resp = await this.stackClient.fetchJSON(
'GET',
uri`/permissions/doctype/${doctype}/shared-by-link`
this.prefix + uri`/permissions/doctype/${doctype}/shared-by-link`
)
return {
...resp,
Expand Down Expand Up @@ -174,7 +190,7 @@ class PermissionCollection extends DocumentCollection {

const resp = await this.stackClient.fetchJSON(
'POST',
`/permissions?${searchParams}`,
`${this.prefix}/permissions?${searchParams}`,
{
data: {
type: 'io.cozy.permissions',
Expand Down Expand Up @@ -257,7 +273,10 @@ class PermissionCollection extends DocumentCollection {
* @returns {Permission} permission
*/
async fetchOwnPermissions() {
const resp = await this.stackClient.fetchJSON('GET', '/permissions/self')
const resp = await this.stackClient.fetchJSON(
'GET',
`${this.prefix}/permissions/self`
)
return {
data: normalizePermission(resp.data),
included: resp.included ? resp.included.map(normalizePermission) : []
Expand Down
136 changes: 136 additions & 0 deletions packages/cozy-stack-client/src/PermissionCollection.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,3 +429,139 @@ describe('getPermissionsFor', () => {
expect(perms2.files.verbs).toEqual(expect.arrayContaining(verbs))
})
})

describe('PermissionCollection with driveId', () => {
const client = new CozyStackClient()
const driveId = 'abc123drive'
const collection = new PermissionCollection('io.cozy.permissions', client, {
driveId
})

beforeEach(() => {
client.fetchJSON.mockReset()
client.fetchJSON.mockResolvedValue({ data: [] })
})

it('should set the correct API prefix for shared drives', () => {
expect(collection.prefix).toEqual('/sharings/drives/abc123drive')
})

describe('get', () => {
it('calls the API with the shared drive prefix', async () => {
client.fetchJSON.mockResolvedValue({
data: { type: 'io.cozy.permissions', id: 'test-perm-id' }
})
await collection.get('test-perm-id')
expect(client.fetchJSON).toHaveBeenCalledWith(
'GET',
'/sharings/drives/abc123drive/permissions/test-perm-id'
)
})
})

describe('create', () => {
it('calls the API with the shared drive prefix', async () => {
client.fetchJSON.mockResolvedValue({
data: { type: 'io.cozy.permissions', id: 'new-perm-id' }
})
await collection.create({ _type: 'io.cozy.permissions', codes: 'code' })
expect(client.fetchJSON).toHaveBeenCalledWith(
'POST',
'/sharings/drives/abc123drive/permissions?codes=code',
{ data: { attributes: {}, type: 'io.cozy.permissions' } }
)
})

it('includes ttl and tiny options', async () => {
client.fetchJSON.mockResolvedValue({
data: { type: 'io.cozy.permissions', id: 'new-perm-id' }
})
await collection.create({
_type: 'io.cozy.permissions',
codes: 'a,b',
ttl: '1D',
tiny: true
})
expect(client.fetchJSON).toHaveBeenCalledWith(
'POST',
'/sharings/drives/abc123drive/permissions?codes=a%2Cb&ttl=1D&tiny=true',
{ data: { attributes: {}, type: 'io.cozy.permissions' } }
)
})
})

describe('add', () => {
it('uses shared drive prefix for permissions document', async () => {
await collection.add(
{
_type: 'io.cozy.permissions',
_id: 'a340d5e0d64711e6b66c5fc9ce1e17c6'
},
fixtures.permission
)
expect(client.fetchJSON).toHaveBeenCalledWith(
'PATCH',
'/sharings/drives/abc123drive/permissions/a340d5e0d64711e6b66c5fc9ce1e17c6',
{
data: {
type: 'io.cozy.permissions',
attributes: {
permissions: fixtures.permission
}
}
}
)
})
})

describe('destroy', () => {
it('calls the API with the shared drive prefix', async () => {
await collection.destroy({ id: 'perm-to-delete' })
expect(client.fetchJSON).toHaveBeenCalledWith(
'DELETE',
'/sharings/drives/abc123drive/permissions/perm-to-delete'
)
})
})

describe('createSharingLink', () => {
it('calls the API with the shared drive prefix', async () => {
client.fetchJSON.mockResolvedValue({
data: { type: 'io.cozy.permissions', id: 'new-perm-id' }
})
const document = { _type: 'io.cozy.files', _id: '1234' }
await collection.createSharingLink(document)
expect(client.fetchJSON).toHaveBeenCalledWith(
'POST',
'/sharings/drives/abc123drive/permissions?codes=code',
{
data: {
type: 'io.cozy.permissions',
attributes: {
permissions: {
files: {
type: 'io.cozy.files',
verbs: ['GET'],
values: ['1234']
}
}
}
}
}
)
})
})

describe('fetchOwnPermissions', () => {
it('calls the API with the shared drive prefix', async () => {
client.fetchJSON.mockResolvedValue({
data: { type: 'io.cozy.permissions', id: 'self-perm' }
})
await collection.fetchOwnPermissions()
expect(client.fetchJSON).toHaveBeenCalledWith(
'GET',
'/sharings/drives/abc123drive/permissions/self'
)
})
})
})
Loading