Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'doc/development/fe_guide/graphql.md')
-rw-r--r--doc/development/fe_guide/graphql.md661
1 files changed, 181 insertions, 480 deletions
diff --git a/doc/development/fe_guide/graphql.md b/doc/development/fe_guide/graphql.md
index 8ae0458e47c..da3a6eff79d 100644
--- a/doc/development/fe_guide/graphql.md
+++ b/doc/development/fe_guide/graphql.md
@@ -63,17 +63,17 @@ the GraphQL extension, follow these steps:
1. Add an `apollo.config.js` file to the root of your `gitlab` local directory.
1. Populate the file with the following content:
- ```javascript
- module.exports = {
- client: {
- includes: ['./app/assets/javascripts/**/*.graphql', './ee/app/assets/javascripts/**/*.graphql'],
- service: {
- name: 'GitLab',
- localSchemaFile: './tmp/tests/graphql/gitlab_schema.graphql',
- },
- },
- };
- ```
+ ```javascript
+ module.exports = {
+ client: {
+ includes: ['./app/assets/javascripts/**/*.graphql', './ee/app/assets/javascripts/**/*.graphql'],
+ service: {
+ name: 'GitLab',
+ localSchemaFile: './tmp/tests/graphql/gitlab_schema.graphql',
+ },
+ },
+ };
+ ```
1. Restart VS Code.
@@ -84,10 +84,8 @@ Our GraphQL API can be explored via GraphiQL at your instance's
[GitLab GraphQL API Reference documentation](../../api/graphql/reference)
where needed.
-You can check all existing queries and mutations on the right side
-of GraphiQL in its **Documentation explorer**. You can also
-write queries and mutations directly on the left tab and check
-their execution by selecting **Execute query** in the upper left:
+To check all existing queries and mutations, on the right side of GraphiQL, select **Documentation explorer**.
+To check the execution of the queries and mutations you've written, in the upper-left corner, select **Execute query**.
![GraphiQL interface](img/graphiql_explorer_v12_4.png)
@@ -107,7 +105,9 @@ Default client accepts two parameters: `resolvers` and `config`.
### Multiple client queries for the same object
-If you are making multiple queries to the same Apollo client object you might encounter the following error: `Cache data may be lost when replacing the someProperty field of a Query object. To address this problem, either ensure all objects of SomeEntityhave an id or a custom merge function`. We are already checking `ID` presence for every GraphQL type that has an `ID`, so this shouldn't be the case. Most likely, the `SomeEntity` type doesn't have an `ID` property, and to fix this warning we need to define a custom merge function.
+If you are making multiple queries to the same Apollo client object you might encounter the following error: `Cache data may be lost when replacing the someProperty field of a Query object. To address this problem, either ensure all objects of SomeEntityhave an id or a custom merge function`. We are already checking `id` presence for every GraphQL type that has an `id`, so this shouldn't be the case (unless you see this warning when running unit tests; in this case please ensure your mocked responses contain an `id` whenever it's requested).
+
+When `SomeEntity` type doesn't have an `id` property in the GraphQL schema, to fix this warning we need to define a custom merge function.
We have some client-wide types with `merge: true` defined in the default client as [`typePolicies`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/lib/graphql.js) (this means that Apollo will merge existing and incoming responses in the case of subsequent queries). Consider adding `SomeEntity` there or defining a custom merge function for it.
@@ -264,9 +264,7 @@ Read more about [Vue Apollo](https://github.com/vuejs/vue-apollo) in the [Vue Ap
### Local state with Apollo
-It is possible to manage an application state with Apollo by using [client-site resolvers](#using-client-side-resolvers)
-or [type policies with reactive variables](#using-type-policies-with-reactive-variables) when creating your default
-client.
+It is possible to manage an application state with Apollo when creating your default client.
#### Using client-side resolvers
@@ -326,6 +324,32 @@ export default {
}
```
+Instead of using `writeQuery`, we can create a type policy that will return `user` on every attempt of reading the `userQuery` from the cache:
+
+```javascript
+const defaultClient = createDefaultClient({}, {
+ cacheConfig: {
+ typePolicies: {
+ Query: {
+ fields: {
+ user: {
+ read(data) {
+ return data || {
+ user: {
+ name: 'John',
+ surname: 'Doe',
+ age: 30
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+});
+```
+
Along with creating local data, we can also extend existing GraphQL types with `@client` fields. This is extremely helpful when we need to mock an API response for fields not yet added to our GraphQL API.
##### Mocking API response with local Apollo cache
@@ -390,122 +414,9 @@ For each attempt to fetch a version, our client fetches `id` and `sha` from the
Read more about local state management with Apollo in the [Vue Apollo documentation](https://vue-apollo.netlify.app/guide/local-state.html#local-state).
-#### Using type policies with reactive variables
-
-Apollo Client 3 offers an alternative to [client-side resolvers](#using-client-side-resolvers) by using
-[reactive variables to store client state](https://www.apollographql.com/docs/react/local-state/reactive-variables/).
-
-**NOTE:**
-We are still learning the best practices for both **type policies** and **reactive vars**.
-Take a moment to improve this guide or [leave a comment](https://gitlab.com/gitlab-org/frontend/rfcs/-/issues/100)
-if you use it!
-
-In the example below we define a `@client` query and its `typedefs`:
-
-```javascript
-// ./graphql/typedefs.graphql
-extend type Query {
- localData: String!
-}
-```
-
-```javascript
-// ./graphql/get_local_data.query.graphql
-query getLocalData {
- localData @client
-}
-```
-
-Similar to resolvers, your `typePolicies` execute when the `@client` query is used. However,
-using `makeVar` triggers every relevant active Apollo query to reactively update when the state
-mutates.
-
-```javascript
-// ./graphql/local_state.js
-
-import { makeVar } from '@apollo/client/core';
-import typeDefs from './typedefs.graphql';
-
-export const createLocalState = () => {
- // set an initial value
- const localDataVar = makeVar('');
-
- const cacheConfig = {
- typePolicies: {
- Query: {
- fields: {
- localData() {
- // obtain current value
- // triggers when `localDataVar` is updated
- return localDataVar();
- },
- },
- },
- },
- };
-
- // methods that update local state
- const localMutations = {
- setLocalData(newData) {
- localDataVar(newData);
- },
- clearData() {
- localDataVar('');
- },
- };
-
- return {
- cacheConfig,
- typeDefs,
- localMutations,
- };
-};
-```
-
-Pass the cache configuration to your Apollo Client:
-
-```javascript
-// index.js
-
-// ...
-import createDefaultClient from '~/lib/graphql';
-import { createLocalState } from './graphql/local_state';
-
-const { cacheConfig, typeDefs, localMutations } = createLocalState();
-
-const apolloProvider = new VueApollo({
- defaultClient: createDefaultClient({}, { cacheConfig, typeDefs }),
-});
-
-return new Vue({
- el,
- apolloProvider,
- provide: {
- // inject local state mutations to your app
- localMutations,
- },
- render(h) {
- return h(MyApp);
- },
-});
-```
-
-Wherever used, the local query updates as the state updates thanks to the **reactive variable**.
-
### Using with Vuex
-When the Apollo Client is used in Vuex and fetched data is stored in the Vuex store, the Apollo Client cache does not need to be enabled. Otherwise we would have data from the API stored in two places - Vuex store and Apollo Client cache. With Apollo's default settings, a subsequent fetch from the GraphQL API could result in fetching data from Apollo cache (in the case where we have the same query and variables). To prevent this behavior, we need to disable Apollo Client cache by passing a valid `fetchPolicy` option to its constructor:
-
-```javascript
-import fetchPolicies from '~/graphql_shared/fetch_policy_constants';
-
-export const gqClient = createGqClient(
- {},
- {
- fetchPolicy: fetchPolicies.NO_CACHE,
- },
-);
-```
+We do not recommend creating new applications with Vuex and Apollo Client combined
### Working on GraphQL-based features when frontend and backend are not in sync
@@ -1218,53 +1129,7 @@ We use [subscriptions](https://www.apollographql.com/docs/react/data/subscriptio
**NOTE:**
We cannot test subscriptions using GraphiQL, because they require an ActionCable client, which GraphiQL does not support at the moment.
-Subscriptions don't require any additional configuration of Apollo Client instance, you can use them in the application right away. To distinguish subscriptions from queries and mutations, we recommend naming them with `.subscription.graphql` extension:
-
-```graphql
-// ~/sidebar/queries/issuable_assignees.subscription.graphql
-
-subscription issuableAssigneesUpdated($issuableId: IssuableID!) {
- issuableAssigneesUpdated(issuableId: $issuableId) {
- ... on Issue {
- assignees {
- nodes {
- ...User
- status {
- availability
- }
- }
- }
- }
- }
-}
-```
-
-When using GraphQL subscriptions in Vue application, we recommend updating existing Apollo query results with [subscribeToMore](https://apollo.vuejs.org/guide/apollo/subscriptions.html#subscribe-to-more) option:
-
-```javascript
-import issuableAssigneesSubscription from '~/sidebar/queries/issuable_assignees.subscription.graphql'
-
-apollo: {
- issuable: {
- query() {
- return assigneesQueries[this.issuableType].query;
- },
- subscribeToMore: {
- // Specify the subscription that will update the query
- document() {
- return issuableAssigneesSubscription;
- },
- variables() {
- return {
- issuableId: convertToGraphQLId(this.issuableClass, this.issuableId),
- };
- },
- },
- },
-},
-```
-
-We would need also to define a field policy similarly like we do it for the [paginated queries](#defining-field-merge-policy)
+Refer to the [Real-time widgets developer guide](../real_time.md) for a comprehensive introduction to subscriptions.
### Best Practices
@@ -1314,73 +1179,6 @@ If you use the RubyMine IDE, and have marked the `tmp` directory as
`gitlab/tmp/tests/graphql`. This will allow the **JS GraphQL** plugin to
automatically find and index the schema.
-#### Testing Apollo components
-
-If we use `ApolloQuery` or `ApolloMutation` in our components, to test their functionality we need to add a stub first:
-
-```javascript
-import { ApolloMutation } from 'vue-apollo';
-
-function createComponent(props = {}) {
- wrapper = shallowMount(MyComponent, {
- sync: false,
- propsData: {
- ...props,
- },
- stubs: {
- ApolloMutation,
- },
- });
-}
-```
-
-`ApolloMutation` component exposes `mutate` method via scoped slot. If we want to test this method, we need to add it to mocks:
-
-```javascript
-const mutate = jest.fn().mockResolvedValue();
-const $apollo = {
- mutate,
-};
-
-function createComponent(props = {}) {
- wrapper = shallowMount(MyComponent, {
- sync: false,
- propsData: {
- ...props,
- },
- stubs: {
- ApolloMutation,
- },
- mocks: {
- $apollo,
- }
- });
-}
-```
-
-Then we can check if `mutate` is called with correct variables:
-
-```javascript
-const mutationVariables = {
- mutation: createNoteMutation,
- update: expect.anything(),
- variables: {
- input: {
- noteableId: 'noteable-id',
- body: 'test',
- discussionId: '0',
- },
- },
-};
-
-it('calls mutation on submitting form ', () => {
- createComponent()
- findReplyForm().vm.$emit('submitForm');
-
- expect(mutate).toHaveBeenCalledWith(mutationVariables);
-});
-```
-
#### Mocking Apollo Client
To test the components with Apollo operations, we need to mock an Apollo Client in our unit tests. We use [`mock-apollo-client`](https://www.npmjs.com/package/mock-apollo-client) library to mock Apollo client and [`createMockApollo` helper](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/frontend/__helpers__/mock_apollo_helper.js) we created on top of it.
@@ -1393,38 +1191,40 @@ import Vue from 'vue';
Vue.use(VueApollo);
-function createMockApolloProvider() {
- return createMockApollo(requestHandlers);
-}
+describe('Some component with Apollo mock', () => {
+ let wrapper;
-function createComponent(options = {}) {
- const { mockApollo } = options;
- ...
- return shallowMount(..., {
- apolloProvider: mockApollo,
- ...
- });
-}
+ function createComponent(options = {}) {
+ wrapper = shallowMount(...);
+ }
+})
```
-After this, you can control whether you need a variable for `mockApollo` and assign it in the appropriate `describe`-scope:
+After this, we need to create a mocked Apollo provider:
```javascript
-describe('Some component', () => {
+import createMockApollo from 'helpers/mock_apollo_helper';
+
+describe('Some component with Apollo mock', () => {
let wrapper;
+ let mockApollo;
- describe('with Apollo mock', () => {
- let mockApollo;
+ function createComponent(options = {}) {
+ mockApollo = createMockApollo(...)
- beforeEach(() => {
- mockApollo = createMockApolloProvider();
- wrapper = createComponent({ mockApollo });
+ wrapper = shallowMount(SomeComponent, {
+ apolloProvider: mockApollo
});
- });
-});
+ }
+
+ afterEach(() => {
+ // we need to ensure we don't have provider persisted between tests
+ mockApollo = null
+ })
+})
```
-In the `createMockApolloProvider`-factory, we need to define an array of _handlers_ for every query or mutation:
+Now, we need to define an array of _handlers_ for every query or mutation. Handlers should be mock functions that return either a correct query response, or an error:
```javascript
import getDesignListQuery from '~/design_management/graphql/queries/get_design_list.query.graphql';
@@ -1435,72 +1235,70 @@ describe('Some component with Apollo mock', () => {
let wrapper;
let mockApollo;
- function createMockApolloProvider() {
- Vue.use(VueApollo);
-
- const requestHandlers = [
- [getDesignListQuery, jest.fn().mockResolvedValue(designListQueryResponse)],
- [permissionsQuery, jest.fn().mockResolvedValue(permissionsQueryResponse)],
- ];
- ...
+ function createComponent(options = {
+ designListHandler: jest.fn().mockResolvedValue(designListQueryResponse)
+ }) {
+ mockApollo = createMockApollo([
+ [getDesignListQuery, options.designListHandler],
+ [permissionsQuery, jest.fn().mockResolvedValue(permissionsQueryResponse)],
+ [moveDesignMutation, jest.fn().mockResolvedValue(moveDesignMutationResponse)],
+ ])
+
+ wrapper = shallowMount(SomeComponent, {
+ apolloProvider: mockApollo
+ });
}
})
```
-After this, we need to create a mock Apollo Client instance using a helper:
+When mocking resolved values, ensure the structure of the response is the same
+as the actual API response. For example, root property should be `data`:
```javascript
-import createMockApollo from 'helpers/mock_apollo_helper';
-
-describe('Some component', () => {
- let wrapper;
-
- function createMockApolloProvider() {
- Vue.use(VueApollo);
-
- const requestHandlers = [
- [getDesignListQuery, jest.fn().mockResolvedValue(designListQueryResponse)],
- [permissionsQuery, jest.fn().mockResolvedValue(permissionsQueryResponse)],
- ];
-
- return createMockApollo(requestHandlers);
- }
-
- function createComponent(options = {}) {
- const { mockApollo } = options;
-
- return shallowMount(Index, {
- apolloProvider: mockApollo,
- });
- }
-
- describe('with Apollo mock', () => {
- let mockApollo;
-
- beforeEach(() => {
- mockApollo = createMockApolloProvider();
- wrapper = createComponent({ mockApollo });
- });
- });
-});
+const designListQueryResponse = {
+ data: {
+ project: {
+ id: '1',
+ issue: {
+ id: 'issue-1',
+ designCollection: {
+ copyState: 'READY',
+ designs: {
+ nodes: [
+ {
+ id: '3',
+ event: 'NONE',
+ filename: 'fox_3.jpg',
+ notesCount: 1,
+ image: 'image-3',
+ imageV432x230: 'image-3',
+ currentUserTodos: {
+ nodes: [],
+ },
+ },
+ ],
+ },
+ versions: {
+ nodes: [],
+ },
+ },
+ },
+ },
+ },
+};
```
-When mocking resolved values, ensure the structure of the response is the same
-as the actual API response. For example, root property should be `data`.
-
When testing queries, keep in mind they are promises, so they need to be _resolved_ to render a result. Without resolving, we can check the `loading` state of the query:
```javascript
it('renders a loading state', () => {
- const mockApollo = createMockApolloProvider();
- const wrapper = createComponent({ mockApollo });
+ const wrapper = createComponent();
expect(wrapper.findComponent(LoadingSpinner).exists()).toBe(true)
});
it('renders designs list', async () => {
- const mockApollo = createMockApolloProvider();
- const wrapper = createComponent({ mockApollo });
+ const wrapper = createComponent();
await waitForPromises()
@@ -1511,17 +1309,10 @@ it('renders designs list', async () => {
If we need to test a query error, we need to mock a rejected value as request handler:
```javascript
-function createMockApolloProvider() {
- ...
- const requestHandlers = [
- [getDesignListQuery, jest.fn().mockRejectedValue(new Error('GraphQL error')],
- ];
- ...
-}
-...
-
it('renders error if query fails', async () => {
- const wrapper = createComponent();
+ const wrapper = createComponent({
+ designListHandler: jest.fn.mockRejectedValue('Houston, we have a problem!')
+ });
await waitForPromises()
@@ -1529,38 +1320,28 @@ it('renders error if query fails', async () => {
})
```
-Request handlers can also be passed to component factory as a parameter.
-
Mutations could be tested the same way:
```javascript
-function createMockApolloProvider({
- moveHandler = jest.fn().mockResolvedValue(moveDesignMutationResponse),
-}) {
- Vue.use(VueApollo);
-
- moveDesignHandler = moveHandler;
-
- const requestHandlers = [
- [getDesignListQuery, jest.fn().mockResolvedValue(designListQueryResponse)],
- [permissionsQuery, jest.fn().mockResolvedValue(permissionsQueryResponse)],
- [moveDesignMutation, moveDesignHandler],
- ];
-
- return createMockApollo(requestHandlers);
-}
-
-function createComponent(options = {}) {
- const { mockApollo } = options;
+ const moveDesignHandlerSuccess = jest.fn().mockResolvedValue(moveDesignMutationResponse)
+
+ function createComponent(options = {
+ designListHandler: jest.fn().mockResolvedValue(designListQueryResponse),
+ moveDesignHandler: moveDesignHandlerSuccess
+ }) {
+ mockApollo = createMockApollo([
+ [getDesignListQuery, options.designListHandler],
+ [permissionsQuery, jest.fn().mockResolvedValue(permissionsQueryResponse)],
+ [moveDesignMutation, moveDesignHandler],
+ ])
+
+ wrapper = shallowMount(SomeComponent, {
+ apolloProvider: mockApollo
+ });
+ }
- return shallowMount(Index, {
- apolloProvider: mockApollo,
- });
-}
-...
it('calls a mutation with correct parameters and reorders designs', async () => {
- const mockApollo = createMockApolloProvider({});
- const wrapper = createComponent({ mockApollo });
+ const wrapper = createComponent();
wrapper.find(VueDraggable).vm.$emit('change', {
moved: {
@@ -1569,7 +1350,7 @@ it('calls a mutation with correct parameters and reorders designs', async () =>
},
});
- expect(moveDesignHandler).toHaveBeenCalled();
+ expect(moveDesignHandlerSuccess).toHaveBeenCalled();
await waitForPromises();
@@ -1619,6 +1400,24 @@ describe('when query times out', () => {
});
```
+Previously, we've used `{ mocks: { $apollo ...}}` on `mount` to test Apollo functionality. This approach is discouraged - proper `$apollo` mocking leaks a lot of implementation details to the tests. Consider replacing it with mocked Apollo provider
+
+```javascript
+wrapper = mount(SomeComponent, {
+ mocks: {
+ // avoid! Mock real graphql queries and mutations instead
+ $apollo: {
+ mutate: jest.fn(),
+ queries: {
+ groups: {
+ loading,
+ },
+ },
+ },
+ },
+});
+```
+
#### Testing `@client` queries
##### Using mock resolvers
@@ -1726,121 +1525,29 @@ query fetchLocalUser {
```javascript
import fetchLocalUserQuery from '~/design_management/graphql/queries/fetch_local_user.query.graphql';
-function createMockApolloProvider() {
- Vue.use(VueApollo);
-
- const requestHandlers = [
- [getDesignListQuery, jest.fn().mockResolvedValue(designListQueryResponse)],
- [permissionsQuery, jest.fn().mockResolvedValue(permissionsQueryResponse)],
- ];
-
- const mockApollo = createMockApollo(requestHandlers, {});
- mockApollo.clients.defaultClient.cache.writeQuery({
- query: fetchLocalUserQuery,
- data: {
- fetchLocalUser: {
- __typename: 'User',
- name: 'Test',
- },
- },
- });
-
- return mockApollo;
-}
-
-function createComponent(options = {}) {
- const { mockApollo } = options;
-
- return shallowMount(Index, {
- apolloProvider: mockApollo,
- });
-}
-```
-
-Sometimes it is necessary to control what the local resolver returns and inspect how it is called by the component. This can be done by mocking your local resolver:
-
-```javascript
-import fetchLocalUserQuery from '~/design_management/graphql/queries/fetch_local_user.query.graphql';
-
-function createMockApolloProvider(options = {}) {
- Vue.use(VueApollo);
- const { fetchLocalUserSpy } = options;
-
- const mockApollo = createMockApollo([], {
- Query: {
- fetchLocalUser: fetchLocalUserSpy,
- },
- });
-
- // Necessary for local resolvers to be activated
- mockApollo.clients.defaultClient.cache.writeQuery({
- query: fetchLocalUserQuery,
- data: {},
- });
-
- return mockApollo;
-}
-```
-
-In the test you can then control what the spy is supposed to do and inspect the component after the request have returned:
-
-```javascript
-describe('My Index test with `createMockApollo`', () => {
+describe('Some component with Apollo mock', () => {
let wrapper;
- let fetchLocalUserSpy;
-
- afterEach(() => {
- wrapper.destroy();
- fetchLocalUserSpy = null;
- });
-
- describe('when loading', () => {
- beforeEach(() => {
- const mockApollo = createMockApolloProvider();
- wrapper = createComponent({ mockApollo });
- });
-
- it('displays the loader', () => {
- // Assess that the loader is present
- });
- });
-
- describe('with data', () => {
- beforeEach(async () => {
- fetchLocalUserSpy = jest.fn().mockResolvedValue(localUserQueryResponse);
- const mockApollo = createMockApolloProvider(fetchLocalUserSpy);
- wrapper = createComponent({ mockApollo });
- await waitForPromises();
- });
-
- it('should fetch data once', () => {
- expect(fetchLocalUserSpy).toHaveBeenCalledTimes(1);
- });
-
- it('displays data', () => {
- // Assess that data is present
- });
- });
-
- describe('with error', () => {
- const error = 'Error!';
-
- beforeEach(async () => {
- fetchLocalUserSpy = jest.fn().mockRejectedValueOnce(error);
- const mockApollo = createMockApolloProvider(fetchLocalUserSpy);
- wrapper = createComponent({ mockApollo });
- await waitForPromises();
- });
+ let mockApollo;
- it('should fetch data once', () => {
- expect(fetchLocalUserSpy).toHaveBeenCalledTimes(1);
+ function createComponent(options = {
+ designListHandler: jest.fn().mockResolvedValue(designListQueryResponse)
+ }) {
+ mockApollo = createMockApollo([...])
+ mockApollo.clients.defaultClient.cache.writeQuery({
+ query: fetchLocalUserQuery,
+ data: {
+ fetchLocalUser: {
+ __typename: 'User',
+ name: 'Test',
+ },
+ },
});
- it('displays the error', () => {
- // Assess that the error is displayed
+ wrapper = shallowMount(SomeComponent, {
+ apolloProvider: mockApollo
});
- });
-});
+ }
+})
```
When you need to configure the mocked apollo client's caching behavior,
@@ -1854,21 +1561,15 @@ const defaultCacheOptions = {
```
```javascript
-function createMockApolloProvider({ props = {}, requestHandlers } = {}) {
- Vue.use(VueApollo);
-
- const mockApollo = createMockApollo(
- requestHandlers,
- {},
- {
- dataIdFromObject: (object) =>
- // eslint-disable-next-line no-underscore-dangle
- object.__typename === 'Requirement' ? object.iid : defaultDataIdFromObject(object),
- },
- );
-
- return mockApollo;
-}
+mockApollo = createMockApollo(
+ requestHandlers,
+ {},
+ {
+ dataIdFromObject: (object) =>
+ // eslint-disable-next-line no-underscore-dangle
+ object.__typename === 'Requirement' ? object.iid : defaultDataIdFromObject(object),
+ },
+);
```
## Handling errors