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)