contentful-migration 4.7.2 → 4.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/CHANGELOG.md +6 -1
  2. package/README.md +237 -139
  3. package/built/bin/cli.js +10 -10
  4. package/built/bin/cli.js.map +1 -1
  5. package/built/bin/lib/config.js +10 -9
  6. package/built/bin/lib/config.js.map +1 -1
  7. package/built/bin/lib/contentful-client/index.js.map +1 -1
  8. package/built/bin/lib/contentful-client/proxy.js.map +1 -1
  9. package/built/bin/lib/render-migration.js.map +1 -1
  10. package/built/bin/lib/steps-errors.js +7 -5
  11. package/built/bin/lib/steps-errors.js.map +1 -1
  12. package/built/bin/usage-params.js +3 -3
  13. package/built/bin/usage-params.js.map +1 -1
  14. package/built/lib/action/action.js.map +1 -1
  15. package/built/lib/action/content-type-annotate.js +27 -0
  16. package/built/lib/action/content-type-annotate.js.map +1 -0
  17. package/built/lib/action/content-type-create.js.map +1 -1
  18. package/built/lib/action/content-type-delete.js.map +1 -1
  19. package/built/lib/action/content-type-publish.js.map +1 -1
  20. package/built/lib/action/content-type-save.js.map +1 -1
  21. package/built/lib/action/content-type-unpublish.js.map +1 -1
  22. package/built/lib/action/content-type-update.js.map +1 -1
  23. package/built/lib/action/editor-layout/editor-layout-change-field-group-control.js +23 -0
  24. package/built/lib/action/editor-layout/editor-layout-change-field-group-control.js.map +1 -0
  25. package/built/lib/action/editor-layout/editor-layout-create-field-group.js +26 -0
  26. package/built/lib/action/editor-layout/editor-layout-create-field-group.js.map +1 -0
  27. package/built/lib/action/editor-layout/editor-layout-create.js +18 -0
  28. package/built/lib/action/editor-layout/editor-layout-create.js.map +1 -0
  29. package/built/lib/action/editor-layout/editor-layout-delete-field-group.js +23 -0
  30. package/built/lib/action/editor-layout/editor-layout-delete-field-group.js.map +1 -0
  31. package/built/lib/action/editor-layout/editor-layout-delete.js +21 -0
  32. package/built/lib/action/editor-layout/editor-layout-delete.js.map +1 -0
  33. package/built/lib/action/editor-layout/editor-layout-move-field.js +24 -0
  34. package/built/lib/action/editor-layout/editor-layout-move-field.js.map +1 -0
  35. package/built/lib/action/editor-layout/editor-layout-update-field-group.js +41 -0
  36. package/built/lib/action/editor-layout/editor-layout-update-field-group.js.map +1 -0
  37. package/built/lib/action/editorinterface-copy.js.map +1 -1
  38. package/built/lib/action/editorinterface-reset.js.map +1 -1
  39. package/built/lib/action/editorinterface-save.js.map +1 -1
  40. package/built/lib/action/editorinterface-update.js.map +1 -1
  41. package/built/lib/action/entry-derive.js +4 -2
  42. package/built/lib/action/entry-derive.js.map +1 -1
  43. package/built/lib/action/entry-field-purge.js.map +1 -1
  44. package/built/lib/action/entry-field-rename.js.map +1 -1
  45. package/built/lib/action/entry-set-tags.js.map +1 -1
  46. package/built/lib/action/entry-transform-to-type.js +3 -1
  47. package/built/lib/action/entry-transform-to-type.js.map +1 -1
  48. package/built/lib/action/entry-transform.js.map +1 -1
  49. package/built/lib/action/entryeditor-configure.js.map +1 -1
  50. package/built/lib/action/entryeditor-reset-to-default.js.map +1 -1
  51. package/built/lib/action/entryeditors-configure.js.map +1 -1
  52. package/built/lib/action/field-action.js.map +1 -1
  53. package/built/lib/action/field-annotate.js +28 -0
  54. package/built/lib/action/field-annotate.js.map +1 -0
  55. package/built/lib/action/field-create.js.map +1 -1
  56. package/built/lib/action/field-move.js.map +1 -1
  57. package/built/lib/action/field-rename.js +4 -0
  58. package/built/lib/action/field-rename.js.map +1 -1
  59. package/built/lib/action/field-update.js.map +1 -1
  60. package/built/lib/action/sidebar-reset-to-default.js.map +1 -1
  61. package/built/lib/action/sidebarwidget-add.js.map +1 -1
  62. package/built/lib/action/sidebarwidget-remove.js.map +1 -1
  63. package/built/lib/action/sidebarwidget-update.js.map +1 -1
  64. package/built/lib/action/sidebarwidget.js.map +1 -1
  65. package/built/lib/action/tag-create.js.map +1 -1
  66. package/built/lib/action/tag-delete.js.map +1 -1
  67. package/built/lib/action/tag-save.js.map +1 -1
  68. package/built/lib/action/tag-update.js.map +1 -1
  69. package/built/lib/entities/content-type.js +247 -17
  70. package/built/lib/entities/content-type.js.map +1 -1
  71. package/built/lib/entities/entry.js.map +1 -1
  72. package/built/lib/entities/link.js.map +1 -1
  73. package/built/lib/entities/tag.js.map +1 -1
  74. package/built/lib/errors/chunks-validation.js.map +1 -1
  75. package/built/lib/errors/payload-validation.js.map +1 -1
  76. package/built/lib/errors/steps-validation.js.map +1 -1
  77. package/built/lib/fetcher.js +14 -12
  78. package/built/lib/fetcher.js.map +1 -1
  79. package/built/lib/intent/base-intent.js +48 -3
  80. package/built/lib/intent/base-intent.js.map +1 -1
  81. package/built/lib/intent/composed-intent.js +56 -6
  82. package/built/lib/intent/composed-intent.js.map +1 -1
  83. package/built/lib/intent/content-type-annotate.js +57 -0
  84. package/built/lib/intent/content-type-annotate.js.map +1 -0
  85. package/built/lib/intent/content-type-create.js +1 -3
  86. package/built/lib/intent/content-type-create.js.map +1 -1
  87. package/built/lib/intent/content-type-update.js +4 -5
  88. package/built/lib/intent/content-type-update.js.map +1 -1
  89. package/built/lib/intent/editor-layout/editor-layout-change-field-group-control.js +57 -0
  90. package/built/lib/intent/editor-layout/editor-layout-change-field-group-control.js.map +1 -0
  91. package/built/lib/intent/editor-layout/editor-layout-change-field-group-id.js +59 -0
  92. package/built/lib/intent/editor-layout/editor-layout-change-field-group-id.js.map +1 -0
  93. package/built/lib/intent/editor-layout/editor-layout-create-field-group.js +54 -0
  94. package/built/lib/intent/editor-layout/editor-layout-create-field-group.js.map +1 -0
  95. package/built/lib/intent/editor-layout/editor-layout-create.js +49 -0
  96. package/built/lib/intent/editor-layout/editor-layout-create.js.map +1 -0
  97. package/built/lib/intent/editor-layout/editor-layout-delete-field-group.js +53 -0
  98. package/built/lib/intent/editor-layout/editor-layout-delete-field-group.js.map +1 -0
  99. package/built/lib/intent/editor-layout/editor-layout-delete.js +45 -0
  100. package/built/lib/intent/editor-layout/editor-layout-delete.js.map +1 -0
  101. package/built/lib/intent/editor-layout/editor-layout-invalid-method.js +38 -0
  102. package/built/lib/intent/editor-layout/editor-layout-invalid-method.js.map +1 -0
  103. package/built/lib/intent/editor-layout/editor-layout-move-field.js +81 -0
  104. package/built/lib/intent/editor-layout/editor-layout-move-field.js.map +1 -0
  105. package/built/lib/intent/editor-layout/editor-layout-update-field-group.js +57 -0
  106. package/built/lib/intent/editor-layout/editor-layout-update-field-group.js.map +1 -0
  107. package/built/lib/intent/editorinterface-copy.js +1 -3
  108. package/built/lib/intent/editorinterface-copy.js.map +1 -1
  109. package/built/lib/intent/editorinterface-reset.js +1 -3
  110. package/built/lib/intent/editorinterface-reset.js.map +1 -1
  111. package/built/lib/intent/editorinterface-update.js +3 -7
  112. package/built/lib/intent/editorinterface-update.js.map +1 -1
  113. package/built/lib/intent/entry-derive.js +2 -7
  114. package/built/lib/intent/entry-derive.js.map +1 -1
  115. package/built/lib/intent/entry-set-tags.js +1 -3
  116. package/built/lib/intent/entry-set-tags.js.map +1 -1
  117. package/built/lib/intent/entry-transform-to-type.js +2 -6
  118. package/built/lib/intent/entry-transform-to-type.js.map +1 -1
  119. package/built/lib/intent/entryeditors-configure.js.map +1 -1
  120. package/built/lib/intent/field-annotate.js +57 -0
  121. package/built/lib/intent/field-annotate.js.map +1 -0
  122. package/built/lib/intent/field-create.js +8 -7
  123. package/built/lib/intent/field-create.js.map +1 -1
  124. package/built/lib/intent/field-delete.js +4 -2
  125. package/built/lib/intent/field-delete.js.map +1 -1
  126. package/built/lib/intent/field-move.js +10 -5
  127. package/built/lib/intent/field-move.js.map +1 -1
  128. package/built/lib/intent/field-rename.js +4 -2
  129. package/built/lib/intent/field-rename.js.map +1 -1
  130. package/built/lib/intent/field-update.js +7 -4
  131. package/built/lib/intent/field-update.js.map +1 -1
  132. package/built/lib/intent/index.js +23 -1
  133. package/built/lib/intent/index.js.map +1 -1
  134. package/built/lib/intent/sidebar-reset-to-default.js.map +1 -1
  135. package/built/lib/intent/sidebarwidget-add.js +7 -10
  136. package/built/lib/intent/sidebarwidget-add.js.map +1 -1
  137. package/built/lib/intent/sidebarwidget-remove.js +6 -8
  138. package/built/lib/intent/sidebarwidget-remove.js.map +1 -1
  139. package/built/lib/intent/sidebarwidget-update.js +7 -10
  140. package/built/lib/intent/sidebarwidget-update.js.map +1 -1
  141. package/built/lib/intent/tag-create.js +1 -3
  142. package/built/lib/intent/tag-create.js.map +1 -1
  143. package/built/lib/intent/tag-delete.js +1 -3
  144. package/built/lib/intent/tag-delete.js.map +1 -1
  145. package/built/lib/intent/tag-update.js +1 -3
  146. package/built/lib/intent/tag-update.js.map +1 -1
  147. package/built/lib/intent-list/index.js +7 -1
  148. package/built/lib/intent-list/index.js.map +1 -1
  149. package/built/lib/intent-validator/content-transform.js.map +1 -1
  150. package/built/lib/intent-validator/content-type-update.js.map +1 -1
  151. package/built/lib/intent-validator/entry-derive.js.map +1 -1
  152. package/built/lib/intent-validator/entry-set-tags.js.map +1 -1
  153. package/built/lib/intent-validator/entry-transform-to-type.js.map +1 -1
  154. package/built/lib/intent-validator/field-movement.js.map +1 -1
  155. package/built/lib/intent-validator/field-update.js.map +1 -1
  156. package/built/lib/intent-validator/schema-validator.js +2 -2
  157. package/built/lib/intent-validator/schema-validator.js.map +1 -1
  158. package/built/lib/intent-validator/tag-update.js.map +1 -1
  159. package/built/lib/interfaces/annotation.js +86 -0
  160. package/built/lib/interfaces/annotation.js.map +1 -0
  161. package/built/lib/migration-chunks/validation/content-type.js +9 -4
  162. package/built/lib/migration-chunks/validation/content-type.js.map +1 -1
  163. package/built/lib/migration-chunks/validation/duplicate-props.js +2 -2
  164. package/built/lib/migration-chunks/validation/duplicate-props.js.map +1 -1
  165. package/built/lib/migration-chunks/validation/editor-layout.js +305 -0
  166. package/built/lib/migration-chunks/validation/editor-layout.js.map +1 -0
  167. package/built/lib/migration-chunks/validation/errors.js +91 -0
  168. package/built/lib/migration-chunks/validation/errors.js.map +1 -1
  169. package/built/lib/migration-chunks/validation/field.js +27 -24
  170. package/built/lib/migration-chunks/validation/field.js.map +1 -1
  171. package/built/lib/migration-chunks/validation/index.js +16 -3
  172. package/built/lib/migration-chunks/validation/index.js.map +1 -1
  173. package/built/lib/migration-chunks/validation/tag.js +4 -2
  174. package/built/lib/migration-chunks/validation/tag.js.map +1 -1
  175. package/built/lib/migration-parser.js +10 -4
  176. package/built/lib/migration-parser.js.map +1 -1
  177. package/built/lib/migration-steps/action-creators.js +165 -1
  178. package/built/lib/migration-steps/action-creators.js.map +1 -1
  179. package/built/lib/migration-steps/dispatch-proxy.js.map +1 -1
  180. package/built/lib/migration-steps/first-external-caller.js.map +1 -1
  181. package/built/lib/migration-steps/index.js +112 -4
  182. package/built/lib/migration-steps/index.js.map +1 -1
  183. package/built/lib/offline-api/index.js +14 -5
  184. package/built/lib/offline-api/index.js.map +1 -1
  185. package/built/lib/offline-api/validator/annotations.js +76 -0
  186. package/built/lib/offline-api/validator/annotations.js.map +1 -0
  187. package/built/lib/offline-api/validator/display-field.js.map +1 -1
  188. package/built/lib/offline-api/validator/editor-interface.js +16 -0
  189. package/built/lib/offline-api/validator/editor-interface.js.map +1 -0
  190. package/built/lib/offline-api/validator/errors.js +21 -0
  191. package/built/lib/offline-api/validator/errors.js.map +1 -1
  192. package/built/lib/offline-api/validator/field-deletion.js +3 -1
  193. package/built/lib/offline-api/validator/field-deletion.js.map +1 -1
  194. package/built/lib/offline-api/validator/field-groups-count.js +34 -0
  195. package/built/lib/offline-api/validator/field-groups-count.js.map +1 -0
  196. package/built/lib/offline-api/validator/schema/content-type-schema.js.map +1 -1
  197. package/built/lib/offline-api/validator/schema/editor-layout-schema.js +46 -0
  198. package/built/lib/offline-api/validator/schema/editor-layout-schema.js.map +1 -0
  199. package/built/lib/offline-api/validator/schema/field-validations-schema.js +1 -1
  200. package/built/lib/offline-api/validator/schema/field-validations-schema.js.map +1 -1
  201. package/built/lib/offline-api/validator/schema/fields-schema.js +13 -5
  202. package/built/lib/offline-api/validator/schema/fields-schema.js.map +1 -1
  203. package/built/lib/offline-api/validator/schema/index.js.map +1 -1
  204. package/built/lib/offline-api/validator/schema/schema-validation.js +42 -1
  205. package/built/lib/offline-api/validator/schema/schema-validation.js.map +1 -1
  206. package/built/lib/offline-api/validator/schema/tag-schema.js.map +1 -1
  207. package/built/lib/offline-api/validator/tag.js.map +1 -1
  208. package/built/lib/offline-api/validator/tags-on-entry.js.map +1 -1
  209. package/built/lib/offline-api/validator/type-change.js.map +1 -1
  210. package/built/lib/utils/deprecated.js.map +1 -1
  211. package/built/lib/utils/editor-layout.js +60 -0
  212. package/built/lib/utils/editor-layout.js.map +1 -0
  213. package/built/lib/utils/is-defined.js.map +1 -1
  214. package/built/lib/utils/should-publish-local-changes.js.map +1 -1
  215. package/docs/validation.md +104 -0
  216. package/index.d.ts +217 -83
  217. package/package.json +22 -3
package/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  ![header](./.github/header.png)
2
+
2
3
  <p align="center">
3
4
  <a href="https://www.contentful.com/slack/">
4
5
  <img src="https://img.shields.io/badge/-Join%20Community%20Slack-2AB27B.svg?logo=slack&maxAge=31557600" alt="Join Contentful Community Slack">
@@ -25,7 +26,6 @@
25
26
  </a>
26
27
  </p>
27
28
 
28
-
29
29
  <p align="center">
30
30
  <a href="https://www.npmjs.com/package/contentful-migration">
31
31
  <img src="https://img.shields.io/npm/v/contentful-migration.svg" alt="NPM">
@@ -67,7 +67,7 @@
67
67
  - [`deriveLinkedEntries(config)` Example](#derivelinkedentriesconfig-example)
68
68
  - [`transformEntriesToType(config)`](#transformentriestotypeconfig)
69
69
  - [`transformEntriesToType` Example](#transformentriestotype-example)
70
- - [`createTag(id[, opts])`](#createtagid-opts)
70
+ - [`createTag(id[, opts, visibility])`](#createtagid-opts-visibility)
71
71
  - [`editTag(id[, opts])`](#edittagid-opts)
72
72
  - [`deleteTag(id)`](#deletetagid)
73
73
  - [`setTagsForEntries(config)`](#settagsforentriesconfig)
@@ -92,11 +92,24 @@
92
92
  - [`configureEntryEditor (widgetNamespace, widgetId[, settings])` : void](#configureentryeditor-widgetnamespace-widgetid-settings--void)
93
93
  - [`configureEntryEditors (EntryEditor[])` : void](#configureentryeditors-entryeditor--void)
94
94
  - [`resetEntryEditorToDefault ()` : void](#resetentryeditortodefault---void)
95
+ - [`createEditorLayout ()` : EditorLayout](#createeditorlayout---editorlayout)
96
+ - [`editEditorLayout ()` : EditorLayout](#editeditorlayout---editorlayout)
97
+ - [`deleteEditorLayout ()` : void](#deleteeditorlayout---void)
95
98
  - [Field](#field)
99
+ - [Editor Layout](#editor-layout)
100
+ - [`moveField(id)` : MovableEditorLayoutItem](#movefieldid--movableeditorlayoutitem)
101
+ - [`createFieldGroup(id[, opts])` : EditorLayoutFieldGroup](#createfieldgroupid-opts--editorlayoutfieldgroup)
102
+ - [`deleteFieldGroup (id)` : void](#deletefieldgroup-id--void)
103
+ - [`changeFieldGroupId (currentId, newId)`](#changefieldgroupid-currentid-newid)
104
+ - [`editFieldGroup (id[, opts])` : EditorLayoutFieldGroup](#editfieldgroup-id-opts--editorlayoutfieldgroup)
105
+ - [Editor Layout Field Group](#editor-layout-field-group)
106
+ - [`createFieldGroup (id[, opts])` : EditorLayoutFieldGroup](#createfieldgroup-id-opts--editorlayoutfieldgroup)
107
+ - [`changeFieldGroupControl (id, widgetNamespace, widgetId[, settings])` : void](#changefieldgroupcontrol-id-widgetnamespace-widgetid-settings--void)
96
108
  - [Validation errors](#validation-errors)
97
109
  - [Example migrations](#example-migrations)
98
110
  - [Writing Migrations in Typescript](#writing-migrations-in-typescript)
99
111
  - [Troubleshooting](#troubleshooting)
112
+ - [Updating Integration tests fixtures](#updating-integration-tests-fixtures)
100
113
  - [Reach out to us](#reach-out-to-us)
101
114
  - [You have questions about how to use this library?](#you-have-questions-about-how-to-use-this-library)
102
115
  - [You found a bug or want to propose a feature?](#you-found-a-bug-or-want-to-propose-a-feature)
@@ -150,6 +163,7 @@ npm install contentful-migration
150
163
  > We moved the CLI version of this tool into our [Contentful CLI](https://github.com/contentful/contentful-cli). This allows our users to use and install only one single CLI tool to get the full Contentful experience.
151
164
  >
152
165
  > Please have a look at the [Contentful CLI migration command documentation](https://github.com/contentful/contentful-cli/tree/master/docs/space/migration) to learn more about how to use this as command line tool.
166
+
153
167
  ### Usage as a library
154
168
 
155
169
  ```javascript
@@ -168,10 +182,10 @@ In your migration description file, export a function that accepts the `migratio
168
182
 
169
183
  ```javascript
170
184
  module.exports = function (migration, context) {
171
- const dog = migration.createContentType('dog');
172
- const name = dog.createField('name');
173
- name.type('Symbol').required(true);
174
- };
185
+ const dog = migration.createContentType('dog')
186
+ const name = dog.createField('name')
187
+ name.type('Symbol').required(true)
188
+ }
175
189
  ```
176
190
 
177
191
  You can also pass the function directly. For example:
@@ -179,10 +193,10 @@ You can also pass the function directly. For example:
179
193
  ```javascript
180
194
  const { runMigration } = require('contentful-migration')
181
195
 
182
- function migrationFunction (migration, context) {
183
- const dog = migration.createContentType('dog');
184
- const name = dog.createField('name');
185
- name.type('Symbol').required(true);
196
+ function migrationFunction(migration, context) {
197
+ const dog = migration.createContentType('dog')
198
+ const name = dog.createField('name')
199
+ name.type('Symbol').required(true)
186
200
  }
187
201
 
188
202
  const options = {
@@ -192,38 +206,40 @@ const options = {
192
206
  }
193
207
 
194
208
  runMigration(options)
195
- .then(() => console.log('Migration Done!'))
196
- .catch((e) => console.error(e))
209
+ .then(() => console.log('Migration Done!'))
210
+ .catch((e) => console.error(e))
197
211
  ```
198
212
 
199
213
  ## Documentation & References
200
214
 
201
215
  ### Configuration
202
216
 
203
- | Name | Default | Type | Description | Required |
204
- |-------------------|------------|---------|-------------------------------------------------------------|----------|
205
- | filePath | | string | The path to the migration file | if `migrationFunction` is not supplied |
206
- | migrationFunction | | function| Specify the migration function directly. See the [expected signature](https://github.com/contentful/contentful-migration/blob/4b9dcae0e7616da9153d0fa481871978595049e7/index.d.ts#L506). | if `filePath` is not supplied |
207
- | spaceId | | string | ID of the space to run the migration script on | true |
208
- | environmentId | `'master'` | string | ID of the environment within the space to run the | false |
209
- | accessToken | | string | The access token to use | true |
210
- | yes | false | boolean | Skips any confirmation before applying the migration,script | false |
211
- | requestBatchSize | 100 | number | Limit for every single request | false |
212
- | headers | | object | Additional headers to attach to the requests | false |
217
+ | Name | Default | Type | Description | Required |
218
+ | ----------------- | ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
219
+ | filePath | | string | The path to the migration file | if `migrationFunction` is not supplied |
220
+ | migrationFunction | | function | Specify the migration function directly. See the [expected signature](https://github.com/contentful/contentful-migration/blob/4b9dcae0e7616da9153d0fa481871978595049e7/index.d.ts#L506). | if `filePath` is not supplied |
221
+ | spaceId | | string | ID of the space to run the migration script on | true |
222
+ | environmentId | `'master'` | string | ID of the environment within the space to run the | false |
223
+ | accessToken | | string | The access token to use | true |
224
+ | yes | false | boolean | Skips any confirmation before applying the migration,script | false |
225
+ | retryLimit | 5 | number | Number of retries before failure (every subsequent retry will increase the timeout to the previous retry by about 1.5 seconds) | false |
226
+ | requestBatchSize | 100 | number | Limit for every single request | false |
227
+ | headers | | object | Additional headers to attach to the requests | false |
213
228
 
214
229
  ### Chaining vs Object notation
215
230
 
216
231
  All methods described below can be used in two flavors:
217
232
 
218
- 1. The chained approach:
233
+ 1. The chained approach:
219
234
 
220
235
  ```javascript
221
- const author = migration.createContentType('author')
236
+ const author = migration
237
+ .createContentType('author')
222
238
  .name('Author')
223
239
  .description('Author of blog posts or pages')
224
240
  ```
225
241
 
226
- 1. The object approach:
242
+ 1. The object approach:
227
243
 
228
244
  ```javascript
229
245
  const author = migration.createContentType('author', {
@@ -246,7 +262,7 @@ Creates a content type with provided `id` and returns a reference to the newly c
246
262
 
247
263
  **`opts : Object`** – Content type definition, with the following options:
248
264
 
249
- - **`name : string`** – Name of the content type.
265
+ - **`name : string`** – Name of the content type.
250
266
  - **`description : string`** – Description of the content type.
251
267
  - **`displayField : string`** – ID of the field to use as the display field for the content type. This is referred to as the "Entry title" in the web application.
252
268
 
@@ -270,27 +286,26 @@ The transform function is expected to return an object with the desired target f
270
286
  - **`from : array`** _(required)_ – Array of the source field IDs
271
287
  - **`to : array`** _(required)_ – Array of the target field IDs
272
288
  - **`transformEntryForLocale : function (fields, locale): object`** _(required)_ – Transformation function to be applied.
273
- - `fields` is an object containing each of the `from` fields. Each field will contain their current localized values (i.e. `fields == {myField: {'en-US': 'my field value'}}`)
274
- - `locale` one of the locales in the space being transformed
275
- The return value must be an object with the same keys as specified in `to`. Their values will be written to the respective entry fields for the current locale (i.e. `{nameField: 'myNewValue'}`). If it returns `undefined`, this the values for this locale on the entry will be left untouched.
289
+ - `fields` is an object containing each of the `from` fields. Each field will contain their current localized values (i.e. `fields == {myField: {'en-US': 'my field value'}}`)
290
+ - `locale` one of the locales in the space being transformed
291
+ The return value must be an object with the same keys as specified in `to`. Their values will be written to the respective entry fields for the current locale (i.e. `{nameField: 'myNewValue'}`). If it returns `undefined`, this the values for this locale on the entry will be left untouched.
276
292
  - **`shouldPublish : bool | 'preserve'`** _(optional)_ – Flag that specifies publishing of target entries, `preserve` will keep current states of the source entries (default `'preserve'`)
277
293
 
278
-
279
294
  ##### `transformEntries` Example
280
295
 
281
296
  ```javascript
282
297
  migration.transformEntries({
283
- contentType: 'newsArticle',
284
- from: ['author', 'authorCity'],
285
- to: ['byline'],
286
- transformEntryForLocale: function (fromFields, currentLocale) {
287
- if (currentLocale === 'de-DE') {
288
- return;
289
- }
290
- const newByline = `${fromFields.author[currentLocale]} ${fromFields.authorCity[currentLocale]}`;
291
- return { byline: newByline };
298
+ contentType: 'newsArticle',
299
+ from: ['author', 'authorCity'],
300
+ to: ['byline'],
301
+ transformEntryForLocale: function (fromFields, currentLocale) {
302
+ if (currentLocale === 'de-DE') {
303
+ return
292
304
  }
293
- });
305
+ const newByline = `${fromFields.author[currentLocale]} ${fromFields.authorCity[currentLocale]}`
306
+ return { byline: newByline }
307
+ }
308
+ })
294
309
  ```
295
310
 
296
311
  For the complete version, please refer to [this example](./examples/12-transform-content.js).
@@ -314,9 +329,11 @@ The derive function is expected to return an object with the desired target fiel
314
329
  - **`derivedFields : array`** _(required)_ – Array of the field IDs on the target content type
315
330
 
316
331
  - **`identityKey: function (fields): string`** _(required)_ - Called once per source entry. Returns the ID used for the derived entry, which is also used for de-duplication so that multiple source entries can link to the same derived entry.
332
+
317
333
  - `fields` is an object containing each of the `from` fields. Each field will contain their current localized values (i.e. `fields == {myField: {'en-US': 'my field value'}}`)
318
334
 
319
335
  - **`deriveEntryForLocale : function (fields, locale): object`** _(required)_ – Function that generates the field values for the derived entry.
336
+
320
337
  - `fields` is an object containing each of the `from` fields. Each field will contain their current localized values (i.e. `fields == {myField: {'en-US': 'my field value'}}`)
321
338
  - `locale` one of the locales in the space being transformed
322
339
 
@@ -324,31 +341,30 @@ The derive function is expected to return an object with the desired target fiel
324
341
 
325
342
  - **`shouldPublish : bool|'preserve'`** _(optional)_ – If true, both the source and the derived entries will be published. If false, both will remain in draft state. If preserve, will keep current states of the source entries (default `true`)
326
343
 
327
-
328
344
  ##### `deriveLinkedEntries(config)` Example
329
345
 
330
346
  ```javascript
331
347
  migration.deriveLinkedEntries({
332
- contentType: 'dog',
333
- derivedContentType: 'owner',
334
- from: ['owner'],
335
- toReferenceField: 'ownerRef',
336
- derivedFields: ['firstName', 'lastName'],
337
- identityKey: async (fromFields) => {
338
- return fromFields.owner['en-US'].toLowerCase().replace(' ', '-');
339
- },
340
- shouldPublish: true,
341
- deriveEntryForLocale: async (inputFields, locale) => {
342
- if (locale !== 'en-US') {
343
- return;
344
- }
345
- const [firstName, lastName] = inputFields.owner[locale].split(' ');
346
- return {
347
- firstName,
348
- lastName
349
- };
348
+ contentType: 'dog',
349
+ derivedContentType: 'owner',
350
+ from: ['owner'],
351
+ toReferenceField: 'ownerRef',
352
+ derivedFields: ['firstName', 'lastName'],
353
+ identityKey: async (fromFields) => {
354
+ return fromFields.owner['en-US'].toLowerCase().replace(' ', '-')
355
+ },
356
+ shouldPublish: true,
357
+ deriveEntryForLocale: async (inputFields, locale) => {
358
+ if (locale !== 'en-US') {
359
+ return
360
+ }
361
+ const [firstName, lastName] = inputFields.owner[locale].split(' ')
362
+ return {
363
+ firstName,
364
+ lastName
350
365
  }
351
- });
366
+ }
367
+ })
352
368
  ```
353
369
 
354
370
  For the complete version of this migration, please refer to [this example](./examples/15-derive-entry-n-to-1.js).
@@ -367,33 +383,34 @@ For the given (source) content type, transforms all its entries according to the
367
383
  - **`updateReferences : bool`** _(optional)_ – Flag that specifies if linking entries should be updated with target entries (default `false`). Note that this flag does not support Rich Text Fields references.
368
384
  - **`removeOldEntries : bool`** _(optional)_ – Flag that specifies if source entries should be deleted (default `false`)
369
385
  - **`transformEntryForLocale : function (fields, locale): object`** _(required)_ – Transformation function to be applied.
370
- - `fields` is an object containing each of the `from` fields. Each field will contain their current localized values (i.e. `fields == {myField: {'en-US': 'my field value'}}`)
371
- - `locale` one of the locales in the space being transformed
386
+
387
+ - `fields` is an object containing each of the `from` fields. Each field will contain their current localized values (i.e. `fields == {myField: {'en-US': 'my field value'}}`)
388
+ - `locale` one of the locales in the space being transformed
372
389
 
373
390
  The return value must be an object with the same keys as specified in the `targetContentType`. Their values will be written to the respective entry fields for the current locale (i.e. `{nameField: 'myNewValue'}`). If it returns `undefined`, this the values for this locale on the entry will be left untouched.
374
391
 
375
392
  ##### `transformEntriesToType` Example
376
393
 
377
394
  ```javascript
378
- const MurmurHash3 = require('imurmurhash');
395
+ const MurmurHash3 = require('imurmurhash')
379
396
 
380
397
  migration.transformEntriesToType({
381
- sourceContentType: 'dog',
382
- targetContentType: 'copycat',
383
- from: ['woofs'],
384
- shouldPublish: false,
385
- updateReferences: false,
386
- removeOldEntries: false,
387
- identityKey: function (fields) {
388
- const value = fields.woofs['en-US'].toString();
389
- return MurmurHash3(value).result().toString();
390
- },
391
- transformEntryForLocale: function (fromFields, currentLocale) {
392
- return {
393
- woofs: `copy - ${fromFields.woofs[currentLocale]}`
394
- };
398
+ sourceContentType: 'dog',
399
+ targetContentType: 'copycat',
400
+ from: ['woofs'],
401
+ shouldPublish: false,
402
+ updateReferences: false,
403
+ removeOldEntries: false,
404
+ identityKey: function (fields) {
405
+ const value = fields.woofs['en-US'].toString()
406
+ return MurmurHash3(value).result().toString()
407
+ },
408
+ transformEntryForLocale: function (fromFields, currentLocale) {
409
+ return {
410
+ woofs: `copy - ${fromFields.woofs[currentLocale]}`
395
411
  }
396
- });
412
+ }
413
+ })
397
414
  ```
398
415
 
399
416
  For the complete version of this migration, please refer to [this example](./examples/22-transform-entries-to-type.js).
@@ -406,7 +423,7 @@ Creates a tag with provided `id` and returns a reference to the newly created ta
406
423
 
407
424
  - **`opts : Object`** – Tag definition, with the following options:
408
425
 
409
- - **`name : string`** – Name of the tag.
426
+ - **`name : string`** – Name of the tag.
410
427
 
411
428
  - **`visibility : 'private' | 'public'`** Tag visibility - defaults to `private`.
412
429
 
@@ -427,22 +444,19 @@ For the given content type, updates the tags that are attached to its entries ac
427
444
 
428
445
  - **`contentType : string`** _(required)_ – Content type ID
429
446
  - **`from : array`** _(required)_ – Array of the source field IDs
430
- - **`setTagsForEntry : function (entryFields, entryTags, apiTags): array`** _(required)_ – Transformation function to be applied.
431
- - `entryFields` is an object containing each of the `from` fields.
432
- - `entryTags` is an array containing link objects of all tags
433
- already attached to the entry.
434
- - `apiTags` is an array containing link objects of all tags
435
- available in the environment.
447
+ - **`setTagsForEntry : function (entryFields, entryTags, apiTags): array`** _(required)_ – Transformation function to be applied. - `entryFields` is an object containing each of the `from` fields. - `entryTags` is an array containing link objects of all tags
448
+ already attached to the entry. - `apiTags` is an array containing link objects of all tags
449
+ available in the environment.
436
450
 
437
451
  ##### `setTagsForEntries` Example
438
452
 
439
- ``` javascript
453
+ ```javascript
440
454
  migration.createTag('department-sf').name('Department: San Francisco')
441
455
  migration.createTag('department-ldn').name('Department: London')
442
456
 
443
457
  const departmentMapping = {
444
458
  'san-francisco': 'department-sf',
445
- 'london': 'department-ldn'
459
+ london: 'department-ldn'
446
460
  }
447
461
 
448
462
  migration.setTagsForEntries({
@@ -452,11 +466,7 @@ migration.setTagsForEntries({
452
466
  const departmentField = entryFields.department['en-US']
453
467
  const newTag = apiTags.find((tag) => tag.sys.id === departmentMapping[departmentField])
454
468
 
455
- return [
456
- ...entryTags,
457
- newTag,
458
- ]
459
-
469
+ return [...entryTags, newTag]
460
470
  }
461
471
  })
462
472
  ```
@@ -470,10 +480,10 @@ module.exports = async function (migration, { makeRequest, spaceId, accessToken
470
480
  const contentType = await makeRequest({
471
481
  method: 'GET',
472
482
  url: `/content_types?sys.id[in]=foo`
473
- });
483
+ })
474
484
 
475
485
  const anyOtherTool = new AnyOtherTool({ spaceId, accessToken })
476
- };
486
+ }
477
487
  ```
478
488
 
479
489
  #### `makeRequest(config)`
@@ -481,8 +491,9 @@ module.exports = async function (migration, { makeRequest, spaceId, accessToken
481
491
  The function used by the migration object to talk to the Contentful Management API. This can be useful if you want to use API features that may not be supported by the `migration` object.
482
492
 
483
493
  `config : Object` - Configuration for the request based on [the Contentful management SDK](https://contentful.github.io/contentful-management.js/contentful-management/7.3.0/globals.html#http)
484
- - `method` : `string` – HTTP method
485
- - `url` : `string` - HTTP endpoint
494
+
495
+ - `method` : `string` HTTP method
496
+ - `url` : `string` - HTTP endpoint
486
497
 
487
498
  ```javascript
488
499
  module.exports = async function (migration, { makeRequest }) {
@@ -490,7 +501,7 @@ module.exports = async function (migration, { makeRequest }) {
490
501
  method: 'GET',
491
502
  url: `/content_types?sys.id[in]=foo`
492
503
  })
493
- };
504
+ }
494
505
  ```
495
506
 
496
507
  #### `spaceId` : `string`
@@ -501,7 +512,6 @@ The space ID that was set for the current migration.
501
512
 
502
513
  The access token that was set for the current migration.
503
514
 
504
-
505
515
  ### Content type
506
516
 
507
517
  For a comprehensive guide to content modelling, please refer to [this guide](https://www.contentful.com/developers/docs/concepts/data-model/).
@@ -510,7 +520,7 @@ For a comprehensive guide to content modelling, please refer to [this guide](htt
510
520
 
511
521
  Creates a field with provided `id`.
512
522
 
513
- **`id : string`** – The ID of the field.
523
+ **`id : string`** – The ID of the field.
514
524
 
515
525
  **`opts : Object`** – Field definition, with the following options:
516
526
 
@@ -547,19 +557,18 @@ Creates a field with provided `id`.
547
557
  Example:
548
558
 
549
559
  ```javascript
550
- validations: [
551
- { in: [ 'Web', 'iOS', 'Android' ] }
552
- ]
560
+ validations: [{ in: ['Web', 'iOS', 'Android'] }]
553
561
  ```
554
562
 
555
563
  _See [The CMA documentation](https://www.contentful.com/developers/docs/references/content-management-api/#/reference/content-types/content-type) for the list of available validations._
564
+
556
565
  - **`localized : boolean`** – Sets the field as localized.
557
566
  - **`disabled : boolean`** – Sets the field as disabled, hence not editable by authors.
558
567
  - **`omitted : boolean`** – Sets the field as omitted, hence not sent in response.
559
568
  - **`deleted : boolean`** – Sets the field as deleted. Requires to have been `omitted` first.
560
569
  _You may prefer using the `deleteField` method._
561
- - **`defaultValue : Object`** – Sets the default value for the field.
562
- Example:
570
+ - **`defaultValue : Object`** – Sets the default value for the field.
571
+ Example:
563
572
 
564
573
  ```javascript
565
574
  defaultValue: {
@@ -605,35 +614,26 @@ Move the field (position of the field in the web editor)
605
614
  - **`.afterField(fieldId)`**
606
615
 
607
616
  Example:
617
+
608
618
  ```javascript
609
619
  module.exports = function (migration) {
610
- const food = migration.editContentType('food');
620
+ const food = migration.editContentType('food')
611
621
 
612
- food.createField('calories')
613
- .type('Number')
614
- .name('How many calories does it have?');
622
+ food.createField('calories').type('Number').name('How many calories does it have?')
615
623
 
616
- food.createField('sugar')
617
- .type('Number')
618
- .name('Amount of sugar');
624
+ food.createField('sugar').type('Number').name('Amount of sugar')
619
625
 
620
- food.createField('vegan')
621
- .type('Boolean')
622
- .name('Vegan friendly');
626
+ food.createField('vegan').type('Boolean').name('Vegan friendly')
623
627
 
624
- food.createField('producer')
625
- .type('Symbol')
626
- .name('Food producer');
628
+ food.createField('producer').type('Symbol').name('Food producer')
627
629
 
628
- food.createField('gmo')
629
- .type('Boolean')
630
- .name('Genetically modified food');
630
+ food.createField('gmo').type('Boolean').name('Genetically modified food')
631
631
 
632
- food.moveField('calories').toTheTop();
633
- food.moveField('sugar').toTheBottom();
634
- food.moveField('producer').beforeField('vegan');
635
- food.moveField('gmo').afterField('vegan');
636
- };
632
+ food.moveField('calories').toTheTop()
633
+ food.moveField('sugar').toTheBottom()
634
+ food.moveField('producer').beforeField('vegan')
635
+ food.moveField('gmo').afterField('vegan')
636
+ }
637
637
  ```
638
638
 
639
639
  #### `changeFieldControl (fieldId, widgetNamespace, widgetId[, settings])` : void
@@ -643,6 +643,7 @@ Changes control interface of given field's ID.
643
643
  **`fieldId : string`** – The ID of the field.
644
644
 
645
645
  **`widgetNamespace : string`** – The namespace of the widget, one of the following values:
646
+
646
647
  - `builtin` (Standard widget)
647
648
  - `app` (Custom App)
648
649
  - `extension` (Custom UI extension)
@@ -677,6 +678,7 @@ Changes control interface of given field's ID.
677
678
  Adds a builtin or custom widget to the sidebar of the content type.
678
679
 
679
680
  **`widgetNamespace: string`** – The namespace of the widget, one of the following values:
681
+
680
682
  - `sidebar-builtin` (Standard widget, default)
681
683
  - `extension` (Custom UI extension)
682
684
 
@@ -691,6 +693,7 @@ Adds a builtin or custom widget to the sidebar of the content type.
691
693
  Updates the configuration of a widget in the sidebar of the content type.
692
694
 
693
695
  **`widgetNamespace: string`** – The namespace of the widget, one of the following values:
696
+
694
697
  - `sidebar-builtin` (Standard widget, default)
695
698
  - `extension` (Custom UI extension)
696
699
 
@@ -703,6 +706,7 @@ Updates the configuration of a widget in the sidebar of the content type.
703
706
  Removes a widget from the sidebar of the content type.
704
707
 
705
708
  **`widgetNamespace: string`** – The namespace of the widget, one of the following values:
709
+
706
710
  - `sidebar-builtin` (Standard widget, default)
707
711
  - `extension` (Custom UI extension)
708
712
 
@@ -734,10 +738,106 @@ Each `EntryEditor` has the following properties:
734
738
 
735
739
  Resets the entry editor of the content type to default.
736
740
 
741
+ #### `createEditorLayout ()` : [EditorLayout](#editor-layout)
742
+
743
+ Creates an empty editor layout for this content type.
744
+
745
+ #### `editEditorLayout ()` : [EditorLayout](#editor-layout)
746
+
747
+ Edits the editor layout for this content type.
748
+
749
+ #### `deleteEditorLayout ()` : void
750
+
751
+ Deletes the editor layout for this content type.
752
+
753
+ #### `setAnnotations(AnnotationId[])`
754
+
755
+ Configure the annotations assigned to this content type. See [annotations documentation](https://www.contentful.com/developers/docs/references/content-management-api/#annotations) for more details on valid `AnnotationId`.
756
+
757
+ #### `clearAnnotations()`
758
+
759
+ Remove all assigned annotations from this content type
760
+
737
761
  ### Field
738
762
 
739
763
  The field object has the same methods as the properties listed in the [`ContentType.createField`](#createfieldid--string-opts--object--field) method.
740
764
 
765
+ In addition the following methods allow to manage field annotations.
766
+
767
+ #### `setAnnotations(AnnotationId[])`
768
+
769
+ Configure the annotations assigned to this field. See [annotations documentation](https://www.contentful.com/developers/docs/references/content-management-api/#annotations) for more details on valid `AnnotationId`.
770
+
771
+ #### `clearAnnotations()`
772
+
773
+ Remove all assigned annotations from this field.
774
+
775
+ ### Editor Layout
776
+
777
+ #### `moveField(id)` : MovableEditorLayoutItem
778
+
779
+ Moves the field with the provided `id`.
780
+
781
+ `moveField(id)` returns a movable editor layout item type which must be called with a direction function:
782
+
783
+ - **`.toTheTopOfFieldGroup(groupId)`**
784
+ - - if no `groupId` is provided, the field will be moved within its group
785
+ - **`.toTheBottomOfFieldGroup(groupId)`**
786
+ - - if no `groupId` is provided, the field will be moved within its group
787
+ - **`.beforeFieldGroup(groupId)`**
788
+ - **`.afterFieldGroup(groupId)`**
789
+ - **`.beforeField(fieldId)`**
790
+ - **`.afterField(fieldId)`**
791
+
792
+ #### `createFieldGroup(id[, opts])` : [EditorLayoutFieldGroup](#editor-layout-field-group)
793
+
794
+ Creates a tab with the provided `id`.
795
+
796
+ **`id : string`** – The ID of the group.
797
+
798
+ **`opts : Object`** – Group settings, with the following options:
799
+
800
+ - **`name : string`** _(required)_ – Group name.
801
+
802
+ #### `deleteFieldGroup (id)` : void
803
+
804
+ Deletes the group with the provided `id` from the editor layout,
805
+ moving its contents to the parent if the group to delete is a field set or to the default tab if it’s a tab.
806
+
807
+ #### `changeFieldGroupId (currentId, newId)`
808
+
809
+ Changes the group’s ID.
810
+
811
+ **`currentId : string`** – The current ID of the group.
812
+
813
+ **`newId : string`** – The new ID for the group.
814
+
815
+ #### `editFieldGroup (id[, opts])` : [EditorLayoutFieldGroup](#editor-layout-field-group)
816
+
817
+ ### Editor Layout Field Group
818
+
819
+ #### `createFieldGroup (id[, opts])` : [EditorLayoutFieldGroup](#editor-layout-field-group)
820
+
821
+ Creates a field set with the provided `id`.
822
+
823
+ **`id : string`** – The ID of the group.
824
+
825
+ **`opts : Object`** – Group settings, with the following options:
826
+
827
+ - **`name : string`** _(required)_ – Group name.
828
+
829
+ #### `changeFieldGroupControl (id, widgetNamespace, widgetId[, settings])` : void
830
+
831
+ Sets the group control for a field group.
832
+
833
+ **`widgetNamespace : string`** – The namespace for the group control. Currently allowed: `builtin`.
834
+ **`widgetId : string`** - The widget ID for the group control. Allowed values: `fieldset`, `topLevelTab`.
835
+ **`settings : Object`** – Field set settings, with the following properties:
836
+
837
+ - **`helpText : string`** – Help text for the field set. Displayed when editing.
838
+ - **`collapsible : boolean`** – Whether the field set can be collapsed when editing.
839
+ - **`collapsedByDefault : string`** – Whether the field set is collapsed when opening the editor.
840
+
741
841
  ## Validation errors
742
842
 
743
843
  You can learn more from the [possible validation errors here](./docs/validation.md).
@@ -757,22 +857,21 @@ const options = {
757
857
  }
758
858
 
759
859
  const migrations = async () => {
760
- await runMigration({...options, ...{filePath: '01-angry-dog.js'}})
761
- await runMigration({...options, ...{filePath: '02-friendly-dog.js'}})
762
- await runMigration({...options, ...{filePath: '03-long-example.js'}})
763
- await runMigration({...options, ...{filePath: '04-steps-errors.js'}})
764
- await runMigration({...options, ...{filePath: '05-plan-errors.js'}})
765
- await runMigration({...options, ...{filePath: '06-delete-field.js'}})
766
- await runMigration({...options, ...{filePath: '07-display-field.js'}})
860
+ await runMigration({ ...options, ...{ filePath: '01-angry-dog.js' } })
861
+ await runMigration({ ...options, ...{ filePath: '02-friendly-dog.js' } })
862
+ await runMigration({ ...options, ...{ filePath: '03-long-example.js' } })
863
+ await runMigration({ ...options, ...{ filePath: '04-steps-errors.js' } })
864
+ await runMigration({ ...options, ...{ filePath: '05-plan-errors.js' } })
865
+ await runMigration({ ...options, ...{ filePath: '06-delete-field.js' } })
866
+ await runMigration({ ...options, ...{ filePath: '07-display-field.js' } })
767
867
  }
768
868
 
769
869
  migrations()
770
-
771
870
  ```
772
871
 
773
872
  ## Writing Migrations in Typescript
774
873
 
775
- You can use Typescript to write your migration files using `ts-node`! First `npm install --save ts-node typescript`,
874
+ You can use Typescript to write your migration files using `ts-node`! First `npm install --save ts-node typescript`,
776
875
  then run your migration with ts-node:
777
876
 
778
877
  ```bash
@@ -780,6 +879,7 @@ node_modules/.bin/ts-node node_modules/.bin/contentful-migration -s $CONTENTFUL_
780
879
  ```
781
880
 
782
881
  An example Typescript migration:
882
+
783
883
  ```typescript
784
884
  import { MigrationFunction } from 'contentful-migration'
785
885
 
@@ -790,9 +890,7 @@ export = function (migration, { makeRequest, spaceId, accessToken }) {
790
890
  })
791
891
 
792
892
  const name = dog.createField('name')
793
- name.name('Name')
794
- .type('Symbol')
795
- .required(true)
893
+ name.name('Name').type('Symbol').required(true)
796
894
  } as MigrationFunction
797
895
  ```
798
896
 
@@ -802,7 +900,7 @@ Here's how it looks inside VS Code:
802
900
 
803
901
  ## Troubleshooting
804
902
 
805
- * Unable to connect to Contentful through your Proxy? Try to set the `rawProxy` option to `true`.
903
+ - Unable to connect to Contentful through your Proxy? Try to set the `rawProxy` option to `true`.
806
904
 
807
905
  ```javascript
808
906
  runMigration({
@@ -813,8 +911,8 @@ runMigration({
813
911
  ```
814
912
 
815
913
  ## Updating Integration tests fixtures
816
- * To add new/update integration tests, you need to set environment variable `NOCK_RECORD=1` which should automatically update fixtures
817
914
 
915
+ - To add new/update integration tests, you need to set environment variable `NOCK_RECORD=1` which should automatically update fixtures
818
916
 
819
917
  ## Reach out to us
820
918