diff --git a/docs/api/cozy-stack-client.md b/docs/api/cozy-stack-client.md index b65916e299..a994a16076 100644 --- a/docs/api/cozy-stack-client.md +++ b/docs/api/cozy-stack-client.md @@ -276,6 +276,45 @@ The returned documents are not paginated by the stack. Extends `DocumentCollection` API along with specific methods for `io.cozy.apps_registry`. **Kind**: global class + +* [AppsRegistryCollection](#AppsRegistryCollection) + * [.all([option])](#AppsRegistryCollection+all) ⇒ Promise.<{data, meta, skip, next}> + * [.getAll(slugs)](#AppsRegistryCollection+getAll) ⇒ Promise.<{data, meta, skip, next}> + * [.get(slug)](#AppsRegistryCollection+get) ⇒ Promise.<{data: object}> + + + +### appsRegistryCollection.all([option]) ⇒ Promise.<{data, meta, skip, next}> +Fetches all apps from the registry. + +**Kind**: instance method of [AppsRegistryCollection](#AppsRegistryCollection) +**Returns**: Promise.<{data, meta, skip, next}> - The JSON API conformant response. +**Throws**: + +- FetchError + + +| Param | Type | Description | +| --- | --- | --- | +| [option] | object | The fetch option | +| [option.limit] | number | Limit of apps to fetch | + + + +### appsRegistryCollection.getAll(slugs) ⇒ Promise.<{data, meta, skip, next}> +Fetches many apps from the registry by ids. + +**Kind**: instance method of [AppsRegistryCollection](#AppsRegistryCollection) +**Returns**: Promise.<{data, meta, skip, next}> - The JSON API conformant response. +**Throws**: + +- FetchError + + +| Param | Type | Description | +| --- | --- | --- | +| slugs | Array.<string> | The slugs of the apps to fetch | + ### appsRegistryCollection.get(slug) ⇒ Promise.<{data: object}> diff --git a/packages/cozy-stack-client/src/AppsRegistryCollection.js b/packages/cozy-stack-client/src/AppsRegistryCollection.js index 7840e458ab..b0ab5b7e6a 100644 --- a/packages/cozy-stack-client/src/AppsRegistryCollection.js +++ b/packages/cozy-stack-client/src/AppsRegistryCollection.js @@ -2,6 +2,7 @@ import { transformRegistryFormatToStackFormat } from 'cozy-client/dist/registry' import DocumentCollection, { normalizeDoc } from './DocumentCollection' import { FetchError } from './errors' +import { dontThrowNotFoundError } from './Collection' export const APPS_REGISTRY_DOCTYPE = 'io.cozy.apps_registry' @@ -20,6 +21,73 @@ class AppsRegistryCollection extends DocumentCollection { this.endpoint = '/registry/' } + /** + * Fetches all apps from the registry. + * + * @param {object} [option] - The fetch option + * @param {number} [option.limit] - Limit of apps to fetch + * @returns {Promise<{data, meta, skip, next}>} The JSON API conformant response. + * @throws {FetchError} + */ + async all({ limit = 1000 } = {}) { + const resp = await this.stackClient.fetchJSON( + 'GET', + `${this.endpoint}?limit=${limit}` + ) + const dataNormalized = resp.data.map(d => { + return normalizeAppFromRegistry( + transformRegistryFormatToStackFormat(d), + this.doctype + ) + }) + + return { + data: dataNormalized, + meta: { + count: resp.meta.count + }, + skip: 0, + next: false + } + } + + /** + * Fetches many apps from the registry by ids. + * + * @param {Array} slugs - The slugs of the apps to fetch + * @returns {Promise<{data, meta, skip, next}>} The JSON API conformant response. + * @throws {FetchError} + */ + async getAll(slugs = []) { + try { + /* + * While waiting for the stack to propose a route that meets this need, we retrieve all the apps then we filter them before returning the result. + * (The limit of 1000 seems more than sufficient for this need) + */ + const resp = await this.stackClient.fetchJSON( + 'GET', + `${this.endpoint}?limit=1000` + ) + const dataFiltered = resp.data.filter(data => slugs.includes(data.slug)) + const dataNormalized = dataFiltered.map(d => { + return normalizeAppFromRegistry( + transformRegistryFormatToStackFormat(d), + this.doctype + ) + }) + return { + data: dataNormalized, + meta: { + count: resp.meta.count + }, + skip: 0, + next: false + } + } catch (error) { + return dontThrowNotFoundError(error) + } + } + /** * Fetches an app from the registry. * diff --git a/packages/cozy-stack-client/src/AppsRegistryCollection.spec.js b/packages/cozy-stack-client/src/AppsRegistryCollection.spec.js index 7113065c80..adc5e9107b 100644 --- a/packages/cozy-stack-client/src/AppsRegistryCollection.spec.js +++ b/packages/cozy-stack-client/src/AppsRegistryCollection.spec.js @@ -8,6 +8,117 @@ import AppsRegistryCollection, { describe(`AppsRegistryCollection`, () => { const client = new CozyStackClient() + describe('getAll', () => { + const collection = new AppsRegistryCollection(client) + const slugs = ['alan', 'caf'] + + beforeAll(() => { + client.fetchJSON.mockReturnValue( + Promise.resolve({ + data: [ + { + id: 'git://github.com/konnectors/alan.git', + slug: 'alan', + _type: 'io.cozy.apps_registry', + latest_version: { + manifest: { + source: 'git://github.com/konnectors/alan.git' + } + } + }, + { + id: 'git://github.com/konnectors/caf.git', + slug: 'caf', + _type: 'io.cozy.apps_registry', + latest_version: { + manifest: { + source: 'git://github.com/konnectors/caf.git' + } + } + } + ], + meta: { count: 2 } + }) + ) + }) + + it('should call the right route', async () => { + await collection.getAll(slugs) + expect(client.fetchJSON).toHaveBeenCalledWith( + 'GET', + '/registry/?limit=1000' + ) + }) + + it('should return the correct data', async () => { + const resp = await collection.getAll(slugs) + expect(resp.data.length).toBeLessThanOrEqual(slugs.length) + expect(resp.data.every(d => slugs.includes(d.slug))).toBe(true) + }) + + it('should return a correct JSON API response', async () => { + const resp = await collection.getAll(slugs) + expect(resp).toConformToJSONAPI() + }) + + it('should return normalized documents', async () => { + const resp = await collection.getAll(slugs) + expect(resp.data[0]).toHaveDocumentIdentity() + expect(resp.data[0]._type).toEqual(APPS_REGISTRY_DOCTYPE) + }) + }) + + describe('all', () => { + const collection = new AppsRegistryCollection(client) + + beforeAll(() => { + client.fetchJSON.mockReturnValue( + Promise.resolve({ + data: [ + { + id: 'git://github.com/konnectors/alan.git', + _id: 'git://github.com/konnectors/alan.git', + _type: 'io.cozy.apps_registry', + latest_version: { + manifest: { + source: 'git://github.com/konnectors/alan.git' + } + } + } + ], + meta: { count: 1 } + }) + ) + }) + + it('should call the right default route', async () => { + await collection.all() + expect(client.fetchJSON).toHaveBeenCalledWith( + 'GET', + '/registry/?limit=1000' + ) + }) + + it('should call the right route with custom limit', async () => { + await collection.all({ limit: 10 }) + expect(client.fetchJSON).toHaveBeenCalledWith( + 'GET', + '/registry/?limit=10' + ) + }) + + it('should return a correct JSON API response', async () => { + const resp = await collection.all() + expect(resp).toConformToJSONAPI() + }) + + it('should return normalized documents', async () => { + const resp = await collection.all() + expect(resp.data[0]).toHaveDocumentIdentity() + expect(resp.data[0]._type).toEqual(APPS_REGISTRY_DOCTYPE) + }) + }) + describe('get', () => { const collection = new AppsRegistryCollection(client)