Repository: api-platform/admin
Pull request #32 (2017-07-18 22:23:45)

Add the possibility to normalize / denormalize data
This PR gives the possibility to:
* convert AOR data to Hydra data before fetching.
* convert Hydra data to AOR data after fetching.

These functionalities are useful if we wish to use `ImageField` and `ImageInput`.

---

Here an example of usage (in `index.js`):

```js
import { FunctionField, ImageField, ImageInput } from 'admin-on-rest/lib/mui';
import React from 'react';
import { render } from 'react-dom';
import HydraAdmin from 'api-platform-admin/lib/hydra/HydraAdmin';
import parseHydraDocumentation from 'api-doc-parser/lib/hydra/parseHydraDocumentation';
import './index.css';

const apiDocumentationParser = entrypoint => parseHydraDocumentation(entrypoint)
  .then(api => {
    api.resources.map(resource => {
      if ('http://schema.org/ImageObject' === resource.id) {
        resource.fields.map(field => {
          if ('http://schema.org/contentUrl' === field.id) {
            field.denormalizeData = value => {
              return { src: value };
            };

            field.fieldComponent = (
              <FunctionField
                key={field.name}
                render={
                  record => (
                    <ImageField key={field.name} record={record} source={`${field.name}.src`}/>
                  )
                }
                source={field.name}
              />
            );

            field.inputComponent = (
              <ImageInput accept="image/*" key={field.name} multiple={false} source={field.name}>
                <ImageField source="src"/>
              </ImageInput>
            );

            field.normalizeData = value => {
              if (value[0] && value[0].rawFile instanceof File) {
                const body = new FormData();
                body.append('file', value[0].rawFile);

                return fetch("http://127.0.0.1:8000/images/upload", { body, method: "POST" })
                  .then(response => response.json());
              }

              return value.src;
            };
          }

          return field;
        });
      }

      return resource;
    });

    return api;
  })
;

render(
  (
    <HydraAdmin apiDocumentationParser={apiDocumentationParser} entrypoint="http://127.0.0.1:8000"/>
  ),
  document.getElementById('root'),
);
```

In my example, I will:
* find every `http://schema.org/ImageObject` resources. For each `http://schema.org/contentUrl` fields, I will use `ImageField` as `fieldComponent` and `ImageInput` as `inputComponent`.
* `ImageInput` will return a `File` instance when form will be submitted. In my example, I will send a multi-part form data to a special action (`http://127.0.0.1:8000/images/upload`). The action will return the ID of the uploaded image. I will "replace" the File instance by the ID in `normalizeData`. 
* As `contentUrl` fields will return only a string, we have to convert Hydra data to AOR data (cf `denormalizeData`).
* use my custom API parser in `HydraAdmin`.

__Note 1__: I choose to send my file via a multi-part form data, but we are totally free to use another solution.

__Note 2__: `fields` are available since [my PR on api-doc-parser](https://github.com/dunglas/api-doc-parser/pull/9). We have to update used version of `api-doc-parser` when it will be released.

__Note 3__: This PR will add a BC break. Now, the first argument of `hydraClient` is an API instance (instead of the entrypoint).

mauchede: A documentation has been added in `README.md`.

Patch:
diff --git a/README.md b/README.md
index 371c970..239e73f 100644
--- a/README.md
+++ b/README.md
@@ -75,41 +75,37 @@ Note: if you don't want to hardcode the API URL, you can [use an environment var
 
 The API Platform's admin parses the Hydra documentation exposed by the API and transforms it to an object data structure. This data structure can be customized to add, remove or customize resources and properties. To do so, we can leverage the `AdminBuilder` component provided by the library. It's a lower level component than the `HydraAdmin` one we used in the previous example. It allows to access to the object storing the structure of admin's screens.
 
+### Use Custom Components
+
 In the following example, we change components used for the `description` property of the `books` resource to ones accepting HTML (respectively `RichTextField` that renders HTML markup and `RichTextInput`, a WYSWYG editor).
 (To use the `RichTextInput`, the `aor-rich-text-input` package is must be installed: `yarn add aor-rich-text-input`).
 
 ```javascript
-import React, { Component } from 'react';
+import React from 'react';
 import { RichTextField } from 'admin-on-rest';
 import RichTextInput from 'aor-rich-text-input';
-import { AdminBuilder, hydraClient } from 'api-platform-admin';
+import HydraAdmin from 'api-platform-admin/lib/hydra/HydraAdmin';
 import parseHydraDocumentation from 'api-doc-parser/lib/hydra/parseHydraDocumentation';
 
 const entrypoint = 'https://demo.api-platform.com';
 
-class App extends Component {
-  state = {api: null};
-
-  componentDidMount() {
-    parseHydraDocumentation(entrypoint).then(api => {
-        const books = api.resources.find(r => 'books' === r.name);
-
-        books.readableFields.find(f => 'description' === f.name).fieldComponent = <RichTextField source="description" key="description"/>;
-        books.writableFields.find(f => 'description' === f.name).inputComponent = <RichTextInput source="description" key="description"/>;
+const apiDocumentationParser = entrypoint => parseHydraDocumentation(entrypoint)
+  .then(api => {
+    api.resources.map(resource => {
+      const books = api.resources.find(r => 'books' === r.name);
+      books.fields.find(f => 'description' === f.name).fieldComponent = <RichTextField source="description" key="description"/>;
+      books.fields.find(f => 'description' === f.name).inputComponent = <RichTextInput source="description" key="description"/>;
 
-        this.setState({api: api});
-      }
-    )
-  }
+      return resource;
+    });
 
-  render() {
-    if (null === this.state.api) return <div>Loading...</div>;
-
-    return <AdminBuilder api={this.state.api} restClient={hydraClient(entrypoint)}/>
-  }
-}
+    return api;
+  })
+;
 
-export default App;
+export default (props) => (
+  <HydraAdmin apiDocumentationParser={apiDocumentationParser} entrypoint={entrypoint}/>
+);
 ```
 
 The `fieldComponent` property of the `Field` class allows to set the component used to render a property in list and show screens.
@@ -119,6 +115,80 @@ Any [field](https://marmelab.com/admin-on-rest/Fields.html) or [input](https://m
 
 To go further, take a look to the "[Including admin-on-rest on another React app](https://marmelab.com/admin-on-rest/CustomApp.html)" documentation page of Admin On Rest to learn how to use directly redux, react-router, and redux-saga along with components provided by this library.
 
+### Manage Files and Images
+
+In the following example, we will:
+* find every [ImageObject](http://schema.org/ImageObject) resources. For each [contentUrl](http://schema.org/contentUrl) fields, we will use [ImageField](https://marmelab.com/admin-on-rest/Fields.html#imagefield) as `field` and [ImageInput](https://marmelab.com/admin-on-rest/Inputs.html#imageinput) as `input`.
+* [ImageInput](https://marmelab.com/admin-on-rest/Inputs.html#imageinput) will return a [File](https://developer.mozilla.org/en/docs/Web/API/File) instance. In this example, we will send a multi-part form data to a special action (`https://demo.api-platform.com/images/upload`). The action will return the ID of the uploaded image. We will "replace" the [File](https://developer.mozilla.org/en/docs/Web/API/File) instance by the ID in `normalizeData`.
+* As `contentUrl` fields will return a string, we have to convert Hydra data to AOR data. This action will be done by `denormalizeData`.
+
+```javascript
+import { FunctionField, ImageField, ImageInput } from 'admin-on-rest/lib/mui';
+import React from 'react';
+import HydraAdmin from 'api-platform-admin/lib/hydra/HydraAdmin';
+import parseHydraDocumentation from 'api-doc-parser/lib/hydra/parseHydraDocumentation';
+
+const entrypoint = 'https://demo.api-platform.com';
+
+const apiDocumentationParser = entrypoint => parseHydraDocumentation(entrypoint)
+  .then(api => {
+    api.resources.map(resource => {
+      if ('http://schema.org/ImageObject' === resource.id) {
+        resource.fields.map(field => {
+          if ('http://schema.org/contentUrl' === field.id) {
+            field.denormalizeData = value => ({
+              src: value
+            });
+
+            field.fieldComponent = (
+              <FunctionField
+                key={field.name}
+                render={
+                  record => (
+                    <ImageField key={field.name} record={record} source={`${field.name}.src`}/>
+                  )
+                }
+                source={field.name}
+              />
+            );
+
+            field.inputComponent = (
+              <ImageInput accept="image/*" key={field.name} multiple={false} source={field.name}>
+                <ImageField source="src"/>
+              </ImageInput>
+            );
+
+            field.normalizeData = value => {
+              if (value[0] && value[0].rawFile instanceof File) {
+                const body = new FormData();
+                body.append('file', value[0].rawFile);
+
+                return fetch(`${entrypoint}/images/upload`, { body, method: 'POST' })
+                  .then(response => response.json());
+              }
+
+              return value.src;
+            };
+          }
+
+          return field;
+        });
+      }
+
+      return resource;
+    });
+
+    return api;
+  })
+;
+
+export default (props) => (
+  <HydraAdmin apiDocumentationParser={apiDocumentationParser} entrypoint={entrypoint}/>
+);
+```
+
+__Note__: In this example, we choose to send the file via a multi-part form data, but you are totally free to use another solution (like `base64`). But keep in mind that multi-part form data is the most efficient solution.
+
 ## Add Authentication Support
 
 Authentication can easily be handled when using the API Platform's admin library.
diff --git a/package.json b/package.json
index efd1346..d274a5c 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,7 @@
   },
   "dependencies": {
     "admin-on-rest": "^1.1",
-    "api-doc-parser": "^0.1",
+    "api-doc-parser": "^0.1.7",
     "babel-runtime": "^6.23",
     "jsonld": "^0.4",
     "lodash.isarray": "^4.0.0",
diff --git a/src/hydra/HydraAdmin.js b/src/hydra/HydraAdmin.js
index 2043965..cf964a7 100644
--- a/src/hydra/HydraAdmin.js
+++ b/src/hydra/HydraAdmin.js
@@ -1,29 +1,44 @@
-import React, {Component} from 'react';
+import apiDocumentationParser from 'api-doc-parser/lib/hydra/parseHydraDocumentation';
 import PropTypes from 'prop-types';
-import parseHydraDocumentation from 'api-doc-parser/lib/hydra/parseHydraDocumentation';
+import React, {Component} from 'react';
 import AdminBuilder from '../AdminBuilder';
-import hydraClient from './hydraClient';
+import restClient from './hydraClient';
+
+class HydraAdmin extends Component {
+  static defaultProps = {
+    apiDocumentationParser,
+    restClient,
+  };
 
-export default class extends Component {
   static propTypes = {
-    entrypoint: PropTypes.string,
+    apiDocumentationParser: PropTypes.func,
+    entrypoint: PropTypes.string.isRequired,
+    restClient: PropTypes.func,
+  };
+
+  state = {
+    api: null,
   };
-  state = {api: null};
 
   componentDidMount() {
-    parseHydraDocumentation(this.props.entrypoint).then(api =>
-      this.setState({api: api}),
-    );
+    this.props
+      .apiDocumentationParser(this.props.entrypoint)
+      .then(api => this.setState({api}));
   }
 
   render() {
-    if (null === this.state.api) return <span>Loading...</span>;
-
-    let props = {...this.props};
-    if (!props.api) props.api = this.state.api;
-    if (!props.restClient)
-      props.restClient = hydraClient(this.props.entrypoint);
+    if (null === this.state.api) {
+      return <span>Loading...</span>;
+    }
 
-    return <AdminBuilder {...props} />;
+    return (
+      <AdminBuilder
+        {...this.props}
+        api={this.state.api}
+        restClient={this.props.restClient(this.state.api)}
+      />
+    );
   }
 }
+
+export default HydraAdmin;
diff --git a/src/hydra/hydraClient.js b/src/hydra/hydraClient.js
index 3c8d3ba..b0a9299 100644
--- a/src/hydra/hydraClient.js
+++ b/src/hydra/hydraClient.js
@@ -1,155 +1,258 @@
 import {
+  CREATE,
+  DELETE,
+  fetchUtils,
   GET_LIST,
-  GET_ONE,
   GET_MANY,
   GET_MANY_REFERENCE,
-  CREATE,
+  GET_ONE,
   UPDATE,
-  DELETE,
-  fetchUtils,
 } from 'admin-on-rest';
-import isPlainObject from 'lodash.isplainobject';
 import isArray from 'lodash.isarray';
+import isPlainObject from 'lodash.isplainobject';
 import fetchHydra from './fetchHydra';
 
 /**
- * Transform a Json-ld document to an Admin On Rest compatible document.
+ * Transforms a JSON-LD document to an admin-on-rest compatible document
  *
- * @param {Number} depth
- * @param {Number} maxDepth
+ * @param {number} maxDepth
+ * @param {number} depth
  */
-export const transformJsonLdToAOR = (maxDepth = 2, depth = 1) => doc => {
-  if (!isPlainObject(doc) && !isArray(doc)) return doc;
-
-  let jsonLdDocument = isArray(doc)
-    ? Array.from(doc)
-    : Object.assign({}, doc, {
-        originId: doc.id,
-        id: doc['@id'],
+export const transformJsonLdDocumentToAORDocument = (
+  maxDepth = 2,
+  depth = 1,
+) => documents => {
+  if (!isPlainObject(documents) && !isArray(documents)) {
+    return documents;
+  }
+
+  documents = isArray(documents)
+    ? Array.from(documents)
+    : Object.assign({}, documents, {
+        originId: documents.id,
+        id: documents['@id'],
       });
 
   if (depth < maxDepth) {
     depth++;
-    if (isArray(jsonLdDocument)) {
-      jsonLdDocument = jsonLdDocument.map(d =>
-        transformJsonLdToAOR(maxDepth, depth)(d),
+
+    if (isArray(documents)) {
+      documents = documents.map(document =>
+        transformJsonLdDocumentToAORDocument(maxDepth, depth)(document),
       );
     } else {
-      Object.keys(jsonLdDocument).forEach(key => {
-        jsonLdDocument[key] = transformJsonLdToAOR(maxDepth, depth)(
-          jsonLdDocument[key],
+      Object.keys(documents).forEach(key => {
+        documents[key] = transformJsonLdDocumentToAORDocument(maxDepth, depth)(
+          documents[key],
         );
       });
     }
   }
 
-  return jsonLdDocument;
+  return documents;
 };
 
 /**
  * Maps admin-on-rest queries to a Hydra powered REST API
  *
  * @see http://www.hydra-cg.com/
+ *
  * @example
- * GET_LIST     => GET http://my.api.url/posts
- * GET_ONE      => GET http://my.api.url/posts/123
- * GET_MANY     => GET http://my.api.url/posts/123, GET http://my.api.url/posts/456, GET http://my.api.url/posts/789
- * UPDATE       => PUT http://my.api.url/posts/123
- * CREATE       => POST http://my.api.url/posts/123
- * DELETE       => DELETE http://my.api.url/posts/123
+ * CREATE   => POST http://my.api.url/posts/123
+ * DELETE   => DELETE http://my.api.url/posts/123
+ * GET_LIST => GET http://my.api.url/posts
+ * GET_MANY => GET http://my.api.url/posts/123, GET http://my.api.url/posts/456, GET http://my.api.url/posts/789
+ * GET_ONE  => GET http://my.api.url/posts/123
+ * UPDATE   => PUT http://my.api.url/posts/123
  */
-export default (apiUrl, httpClient = fetchHydra) => {
+export default ({entrypoint, resources = []}, httpClient = fetchHydra) => {
   /**
-   * @param {String} type One of the constants appearing at the top if this file, e.g. 'UPDATE'
-   * @param {String} resource Name of the resource to fetch, e.g. 'posts'
-   * @param {Object} params The REST request params, depending on the type
-   * @returns {Object} { url, options } The HTTP request parameters
+   * @param {string} resource
+   * @param {Object} data
+   *
+   * @returns {Promise}
    */
-  const convertRESTRequestToHTTP = (type, resource, params) => {
-    let url = '';
-    const options = {};
+  const convertAORDataToHydraData = (resource, data) => {
+    resource = resources.find(({name}) => resource === name);
+    if (undefined === resource) {
+      return Promise.resolve(data);
+    }
+
+    const fieldData = [];
+    resource.fields.forEach(({name, normalizeData}) => {
+      if (!(name in data) || undefined === normalizeData) {
+        return;
+      }
+
+      fieldData[name] = normalizeData(data[name]);
+    });
+
+    const fieldDataKeys = Object.keys(fieldData);
+    const fieldDataValues = Object.values(fieldData);
+
+    return Promise.all(fieldDataValues).then(fieldData => {
+      const object = {};
+      for (let i = 0; i < fieldDataKeys.length; i++) {
+        object[fieldDataKeys[i]] = fieldData[i];
+      }
+
+      return {...data, ...object};
+    });
+  };
+
+  /**
+   * @param {string} type
+   * @param {string} resource
+   * @param {Object} params
+   *
+   * @returns {Object}
+   */
+  const convertAORRequestToHydraRequest = (type, resource, params) => {
     switch (type) {
+      case CREATE:
+        return convertAORDataToHydraData(resource, params.data).then(data => ({
+          options: {
+            body: JSON.stringify(data),
+            method: 'POST',
+          },
+          url: `${entrypoint}/${resource}`,
+        }));
+
+      case DELETE:
+        return Promise.resolve({
+          options: {
+            method: 'DELETE',
+          },
+          url: entrypoint + params.id,
+        });
+
       case GET_LIST: {
-        const {page} = params.pagination;
-        const {field, order} = params.sort;
-        const query = {
-          ...params.filter,
-          sort: field,
-          order: order,
-          page: page,
-        };
-        url = `${apiUrl}/${resource}?${fetchUtils.queryParameters(query)}`;
-        break;
+        const {pagination: {page}} = params;
+
+        return Promise.resolve({
+          options: {},
+          url: `${entrypoint}/${resource}?${fetchUtils.queryParameters({
+            ...params.filter,
+            page,
+          })}`,
+        });
       }
-      case GET_ONE:
-        url = apiUrl + params.id;
-        break;
+
       case GET_MANY_REFERENCE:
-        url = `${apiUrl}/${resource}?${fetchUtils.queryParameters({
-          [params.target]: params.id,
-        })}`;
-        break;
+        return Promise.resolve({
+          options: {},
+          url: `${entrypoint}/${resource}?${fetchUtils.queryParameters({
+            [params.target]: params.id,
+          })}`,
+        });
+
+      case GET_ONE:
+        return Promise.resolve({
+          options: {},
+          url: entrypoint + params.id,
+        });
+
       case UPDATE:
-        url = apiUrl + params.id;
-        options.method = 'PUT';
-        options.body = JSON.stringify(params.data);
-        break;
-      case CREATE:
-        url = `${apiUrl}/${resource}`;
-        options.method = 'POST';
-        options.body = JSON.stringify(params.data);
-        break;
-      case DELETE:
-        url = apiUrl + params.id;
-        options.method = 'DELETE';
-        break;
+        return convertAORDataToHydraData(resource, params.data).then(data => ({
+          options: {
+            body: JSON.stringify(data),
+            method: 'PUT',
+          },
+          url: entrypoint + params.id,
+        }));
+
       default:
         throw new Error(`Unsupported fetch action type ${type}`);
     }
-    return {url, options};
   };
 
   /**
-   * @param {Object} response - HTTP response from fetch()
-   * @param {String} type     - One of the constants appearing at the top if this file, e.g. 'UPDATE'
-   * @param {String} resource -  Name of the resource to fetch, e.g. 'posts'
-   * @param {Object} params   - The REST request params, depending on the type
-   * @returns {Object}        - REST response
+   * @param {string} resource
+   * @param {Object} data
+   *
+   * @returns {Promise}
+   */
+  const convertHydraDataToAORData = (resource, data) => {
+    resource = resources.find(({name}) => resource === name);
+    if (undefined === resource) {
+      return Promise.resolve(data);
+    }
+
+    const fieldData = {};
+    resource.fields.forEach(({name, denormalizeData}) => {
+      if (!(name in data) || undefined === denormalizeData) {
+        return;
+      }
+
+      fieldData[name] = denormalizeData(data[name]);
+    });
+
+    const fieldDataKeys = Object.keys(fieldData);
+    const fieldDataValues = Object.values(fieldData);
+
+    return Promise.all(fieldDataValues).then(fieldData => {
+      const object = {};
+      for (let i = 0; i < fieldDataKeys.length; i++) {
+        object[fieldDataKeys[i]] = fieldData[i];
+      }
+
+      return {...data, ...object};
+    });
+  };
+
+  /**
+   * @param {Object} response
+   * @param {string} resource
+   * @param {string} type
+   *
+   * @returns {Promise}
    */
-  const convertHTTPResponseToREST = (response, type, resource, params) => {
+  const convertHydraResponseToAORResponse = (type, resource, response) => {
     switch (type) {
       case GET_LIST:
         // TODO: support other prefixes than "hydra:"
-        return {
-          data: response.json['hydra:member'].map(transformJsonLdToAOR()),
-          total: response.json['hydra:totalItems'],
-        };
+        return Promise.resolve(
+          response.json['hydra:member'].map(
+            transformJsonLdDocumentToAORDocument(),
+          ),
+        )
+          .then(data =>
+            Promise.all(
+              data.map(data => convertHydraDataToAORData(resource, data)),
+            ),
+          )
+          .then(data => ({data, total: response.json['hydra:totalItems']}));
+
       default:
-        return {data: transformJsonLdToAOR()(response.json)};
+        return Promise.resolve(
+          transformJsonLdDocumentToAORDocument()(response.json),
+        )
+          .then(data => convertHydraDataToAORData(resource, data))
+          .then(data => ({data}));
     }
   };
 
   /**
-   * @param {string} type Request type, e.g GET_LIST
-   * @param {string} resource Resource name, e.g. "posts"
-   * @param {Object} payload Request parameters. Depends on the request type
-   * @returns {Promise} the Promise for a REST response
+   * @param {string} type
+   * @param {string} resource
+   * @param {Object} params
+   *
+   * @returns {Promise}
    */
-  return (type, resource, params) => {
+  const fetchApi = (type, resource, params) => {
     // Hydra doesn't handle WHERE IN requests, so we fallback to calling GET_ONE n times instead
-    if (type === GET_MANY) {
+    if (GET_MANY === type) {
       return Promise.all(
-        params.ids.map(id => httpClient(apiUrl + id)),
-      ).then(responses => {
-        const restResponse = {data: []};
-        restResponse.data = responses.map(response => response.json);
-        restResponse.data.map(transformJsonLdToAOR());
-        return restResponse;
-      });
+        params.ids.map(id => fetchApi(GET_ONE, resource, {id})),
+      ).then(responses => ({data: responses}));
     }
-    const {url, options} = convertRESTRequestToHTTP(type, resource, params);
-    return httpClient(url, options).then(response =>
-      convertHTTPResponseToREST(response, type, resource, params),
-    );
+
+    return convertAORRequestToHydraRequest(type, resource, params)
+      .then(({url, options}) => httpClient(url, options))
+      .then(response =>
+        convertHydraResponseToAORResponse(type, resource, response),
+      );
   };
+
+  return fetchApi;
 };


Test patch:
diff --git a/src/hydra/hydraClient.test.js b/src/hydra/hydraClient.test.js
index e2c4c06..197b443 100644
--- a/src/hydra/hydraClient.test.js
+++ b/src/hydra/hydraClient.test.js
@@ -1,4 +1,4 @@
-import {transformJsonLdToAOR} from './hydraClient';
+import {transformJsonLdDocumentToAORDocument} from './hydraClient';
 
 describe('map a json-ld document to an admin on rest compatible document', () => {
   const jsonLdDocument = {
@@ -36,14 +36,14 @@ describe('map a json-ld document to an admin on rest compatible document', () =>
   };
 
   describe('transform only the main document when called with a max depth of 1', () => {
-    const AORDocument = transformJsonLdToAOR(1)(jsonLdDocument);
+    const AORDocument = transformJsonLdDocumentToAORDocument(1)(jsonLdDocument);
 
     test('add an id property equal to the original @id property', () => {
       expect(AORDocument.id).toEqual(jsonLdDocument['@id']);
     });
 
     test('preserve the previous id property value in a new originId property', () => {
-      expect(AORDocument.originId).toEqual(jsonLdDocument['id']);
+      expect(AORDocument.originId).toEqual(jsonLdDocument.id);
     });
 
     test('do not alter the embedded document', () => {
@@ -54,7 +54,7 @@ describe('map a json-ld document to an admin on rest compatible document', () =>
   });
 
   describe('transform the embedded document when called with a max depth of 2', () => {
-    const AORDocument = transformJsonLdToAOR(2)(jsonLdDocument);
+    const AORDocument = transformJsonLdDocumentToAORDocument(2)(jsonLdDocument);
 
     test('add an id property on the embedded document equal to the @id property of the embedded document', () => {
       expect(AORDocument.itemReviewed.id).toEqual(
@@ -64,7 +64,7 @@ describe('map a json-ld document to an admin on rest compatible document', () =>
   });
 
   describe('transform the embedded document collection when called with a max depth of 3', () => {
-    const AORDocument = transformJsonLdToAOR(3)(jsonLdDocument);
+    const AORDocument = transformJsonLdDocumentToAORDocument(3)(jsonLdDocument);
 
     test('add an id property on each document of an embedded collection equal to the @id property', () => {
       AORDocument.comment.forEach(comment => {