contentful-migration
contentful-migration – content model migration tool
Describe and execute changes to your content model and transform entry content.
<p>
</a>
</p>
<p>
<a rel="nofollow noopener" target="_blank" href="https://www.npmjs.com/package/contentful-migration"></p>
<p>
</a><br /> <br /> <a rel="nofollow noopener" target="_blank" href="http://npm-stat.com/charts.html?package=contentful-migration"></p>
<p>
</a><br />
</p>
<p>
<strong>What is Contentful?</strong><br /> <a rel="nofollow noopener" target="_blank" href="https://www.contentful.com/">Contentful</a> provides content infrastructure for digital teams to power websites, apps, and devices. Unlike a CMS, Contentful was built to integrate with the modern software stack. It offers a central hub for structured content, powerful management and delivery APIs, and a customizable web app that enable developers and content creators to ship their products faster.
</p>
<p>
Table of contents
</p>
<ul dir="auto">
<li>
<a rel="nofollow noopener" target="_blank" href="#contentful-migration---content-model-migration-tool">contentful-migration – content model migration tool</a></p> <ul dir="auto">
<li>
<a rel="nofollow noopener" target="_blank" href="#core-features">Core Features</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#pre-requisites--installation">Pre-requisites && Installation</a></p> <ul dir="auto">
<li>
<a rel="nofollow noopener" target="_blank" href="#pre-requisites">Pre-requisites</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#installation">Installation</a>
</li>
</ul>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#usage">Usage</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#exclamation-usage-as-cli">❗ Usage as CLI</a></p> <ul dir="auto">
<li>
<a rel="nofollow noopener" target="_blank" href="#usage-as-a-library">Usage as a library</a>
</li>
</ul>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#documentation--references">Documentation & References</a></p> <ul dir="auto">
<li>
<a rel="nofollow noopener" target="_blank" href="#configuration">Configuration</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#chaining-vs-object-notation">Chaining vs Object notation</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#migration"><code>migration</code></a></p> <ul dir="auto">
<li>
<a rel="nofollow noopener" target="_blank" href="#createcontenttypeid-opts--contenttype"><code>createContentType(id[, opts])</code> : ContentType</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#editcontenttypeid-opts--contenttype"><code>editContentType(id[, opts])</code> : ContentType</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#deletecontenttypeid"><code>deleteContentType(id)</code></a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#transformentriesconfig"><code>transformEntries(config)</code></a></p> <ul dir="auto">
<li>
<a rel="nofollow noopener" target="_blank" href="#transformentries-example"><code>transformEntries</code> Example</a>
</li>
</ul>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#derivelinkedentriesconfig"><code>deriveLinkedEntries(config)</code></a></p> <ul dir="auto">
<li>
<a rel="nofollow noopener" target="_blank" href="#derivelinkedentriesconfig-example"><code>deriveLinkedEntries(config)</code> Example</a>
</li>
</ul>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#transformentriestotypeconfig"><code>transformEntriesToType(config)</code></a></p> <ul dir="auto">
<li>
<a rel="nofollow noopener" target="_blank" href="#transformentriestotype-example"><code>transformEntriesToType</code> Example</a>
</li>
</ul>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#createtagid-opts-visibility"><code>createTag(id[, opts, visibility])</code></a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#edittagid-opts"><code>editTag(id[, opts])</code></a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#deletetagid"><code>deleteTag(id)</code></a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#settagsforentriesconfig"><code>setTagsForEntries(config)</code></a></p> <ul dir="auto">
<li>
<a rel="nofollow noopener" target="_blank" href="#settagsforentries-example"><code>setTagsForEntries</code> Example</a>
</li>
</ul>
</li>
</ul>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#context"><code>context</code></a></p> <ul dir="auto">
<li>
<a rel="nofollow noopener" target="_blank" href="#makerequestconfig"><code>makeRequest(config)</code></a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#spaceid--string"><code>spaceId</code> : <code>string</code></a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#accesstoken--string"><code>accessToken</code> : <code>string</code></a>
</li>
</ul>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#content-type">Content type</a></p> <ul dir="auto">
<li>
<a rel="nofollow noopener" target="_blank" href="#createfieldid-opts--field"><code>createField(id[, opts])</code> : Field</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#editfieldid-opts--field"><code>editField(id[, opts])</code> : Field</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#deletefieldid--void"><code>deleteField(id)</code> : void</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#changefieldid-currentid-newid--void"><code>changeFieldId (currentId, newId)</code> : void</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#movefield-id--movablefield"><code>moveField (id)</code> : MovableField</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#changefieldcontrol-fieldid-widgetnamespace-widgetid-settings--void"><code>changeFieldControl (fieldId, widgetNamespace, widgetId[, settings])</code> : void</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#resetfieldcontrol-fieldid--void"><code>resetFieldControl (fieldId)</code> : void</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#copyfieldcontrol-sourcefieldid-destinationfieldid--void"><code>copyFieldControl (sourceFieldId, destinationFieldId)</code> : void</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#addsidebarwidget-widgetnamespace-widgetid-settings-insertbeforewidgetid--void"><code>addSidebarWidget (widgetNamespace, widgetId[, settings, insertBeforeWidgetId])</code> : void</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#updatesidebarwidget-widgetnamespace-widgetid-settings--void"><code>updateSidebarWidget (widgetNamespace, widgetId, settings)</code> : void</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#removesidebarwidget-widgetnamespace-widgetid--void"><code>removeSidebarWidget (widgetNamespace, widgetId)</code> : void</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#resetsidebartodefault---void"><code>resetSidebarToDefault ()</code> : void</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#configureentryeditor-widgetnamespace-widgetid-settings--void"><code>configureEntryEditor (widgetNamespace, widgetId[, settings])</code> : void</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#configureentryeditors-entryeditor--void"><code>configureEntryEditors (EntryEditor[])</code> : void</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#resetentryeditortodefault---void"><code>resetEntryEditorToDefault ()</code> : void</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#createeditorlayout---editorlayout"><code>createEditorLayout ()</code> : EditorLayout</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#editeditorlayout---editorlayout"><code>editEditorLayout ()</code> : EditorLayout</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#deleteeditorlayout---void"><code>deleteEditorLayout ()</code> : void</a>
</li>
</ul>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#field">Field</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#editor-layout">Editor Layout</a></p> <ul dir="auto">
<li>
<a rel="nofollow noopener" target="_blank" href="#movefieldid--movableeditorlayoutitem"><code>moveField(id)</code> : MovableEditorLayoutItem</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#createfieldgroupid-opts--editorlayoutfieldgroup"><code>createFieldGroup(id[, opts])</code> : EditorLayoutFieldGroup</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#deletefieldgroup-id--void"><code>deleteFieldGroup (id)</code> : void</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#changefieldgroupid-currentid-newid"><code>changeFieldGroupId (currentId, newId)</code></a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#editfieldgroup-id-opts--editorlayoutfieldgroup"><code>editFieldGroup (id[, opts])</code> : EditorLayoutFieldGroup</a>
</li>
</ul>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#editor-layout-field-group">Editor Layout Field Group</a></p> <ul dir="auto">
<li>
<a rel="nofollow noopener" target="_blank" href="#createfieldgroup-id-opts--editorlayoutfieldgroup"><code>createFieldGroup (id[, opts])</code> : EditorLayoutFieldGroup</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#changefieldgroupcontrol-id-widgetnamespace-widgetid-settings--void"><code>changeFieldGroupControl (id, widgetNamespace, widgetId[, settings])</code> : void</a>
</li>
</ul>
</li>
</ul>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#validation-errors">Validation errors</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#example-migrations">Example migrations</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#writing-migrations-in-typescript">Writing Migrations in Typescript</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#troubleshooting">Troubleshooting</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#updating-integration-tests-fixtures">Updating Integration tests fixtures</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#reach-out-to-us">Reach out to us</a></p> <ul dir="auto">
<li>
<a rel="nofollow noopener" target="_blank" href="#you-have-questions-about-how-to-use-this-library">You have questions about how to use this library?</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#you-found-a-bug-or-want-to-propose-a-feature">You found a bug or want to propose a feature?</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#you-need-to-share-confidential-information-or-have-other-questions">You need to share confidential information or have other questions?</a>
</li>
</ul>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#get-involved">Get involved</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#license">License</a>
</li>
<li>
<a rel="nofollow noopener" target="_blank" href="#code-of-conduct">Code of Conduct</a>
</li>
</ul>
</li>
</ul>
<h2 dir="auto">
<a rel="nofollow noopener" target="_blank" id="user-content-core-features" class="anchor" aria-hidden="true" href="#core-features"></a>Core Features
</h2>
<ul dir="auto">
<li>
Content type <ul dir="auto">
<li>
Edit Content type
</li>
<li>
Create a Content type
</li>
</ul>
</li>
<li>
Entries <ul dir="auto">
<li>
Transform Entries for a Given Content type
</li>
<li>
Derives a new entry and sets up a reference to it on the source entry
</li>
<li>
Updates tags on entries for a given Content Type
</li>
</ul>
</li>
<li>
Fields <ul dir="auto">
<li>
Create a field
</li>
<li>
Edit a field
</li>
<li>
Delete a field
</li>
<li>
Rename a field
</li>
<li>
Change a field’s control
</li>
<li>
Reset a field’s control
</li>
<li>
Copy a field’s control
</li>
<li>
Move field
</li>
</ul>
</li>
<li>
Tags <ul dir="auto">
<li>
Create a Tag
</li>
<li>
Rename a Tag
</li>
<li>
Delete a Tag
</li>
</ul>
</li>
</ul>
<h2 dir="auto">
<a rel="nofollow noopener" target="_blank" id="user-content-pre-requisites--installation" class="anchor" aria-hidden="true" href="#pre-requisites--installation"></a>Pre-requisites && Installation
</h2>
<h3 dir="auto">
<a rel="nofollow noopener" target="_blank" id="user-content-pre-requisites" class="anchor" aria-hidden="true" href="#pre-requisites"></a>Pre-requisites
</h3>
<ul dir="auto">
<li>
Node LTS
</li>
</ul>
<h3 dir="auto">
<a rel="nofollow noopener" target="_blank" id="user-content-installation" class="anchor" aria-hidden="true" href="#installation"></a>Installation
</h3>
<pre>npm install contentful-migration</pre>
<h2 dir="auto">
<a rel="nofollow noopener" target="_blank" id="user-content-usage" class="anchor" aria-hidden="true" href="#usage"></a>Usage
</h2>
<h2 dir="auto">
<a rel="nofollow noopener" target="_blank" id="user-content-exclamation-usage-as-cli" class="anchor" aria-hidden="true" href="#exclamation-usage-as-cli"></a>❗ Usage as CLI
</h2>
<blockquote>
<p>
We moved the CLI version of this tool into our Contentful CLI. This allows our users to use and install only one single CLI tool to get the full Contentful experience.<br /> Please have a look at the Contentful CLI migration command documentation to learn more about how to use this as command line tool.
</p>
</blockquote>
<h3 dir="auto">
<a rel="nofollow noopener" target="_blank" id="user-content-usage-as-a-library" class="anchor" aria-hidden="true" href="#usage-as-a-library"></a>Usage as a library
</h3>
<pre>const { runMigration } = require('contentful-migration')
const options = { filePath: ‘<migration-file-path>’, spaceId: ‘<space-id>’, accessToken: ‘<access-token>’ } runMigration(options) .then(() => console.log(‘Migration Done!’)) .catch((e) => console.error(e))
<p>
In your migration description file, export a function that accepts the <code>migration</code> object as its argument. For example:
</p>
<pre>module.exports = function (migration, context) {
const dog = migration.createContentType(‘dog’) const name = dog.createField(’name’) name.type(‘Symbol’).required(true) }
<p>
You can also pass the function directly. For example:
</p>
<pre>const { runMigration } = require('contentful-migration')
function migrationFunction(migration, context) { const dog = migration.createContentType(‘dog’) const name = dog.createField(’name’) name.type(‘Symbol’).required(true) }
const options = { migrationFunction, spaceId: ‘<space-id>’, accessToken: ‘<access-token>’ }
runMigration(options) .then(() => console.log(‘Migration Done!’)) .catch((e) => console.error(e))
<h2 dir="auto">
<a rel="nofollow noopener" target="_blank" id="user-content-documentation--references" class="anchor" aria-hidden="true" href="#documentation--references"></a>Documentation & References
</h2>
<h3 dir="auto">
<a rel="nofollow noopener" target="_blank" id="user-content-configuration" class="anchor" aria-hidden="true" href="#configuration"></a>Configuration
</h3>
<p>
Name<br /> Default<br /> Type<br /> Description<br /> Required
</p>
<p>
filePath
</p>
<p>
string<br /> The path to the migration file<br /> if <code>migrationFunction</code> is not supplied
</p>
<p>
migrationFunction
</p>
<p>
function<br /> Specify the migration function directly. See the expected signature.<br /> if <code>filePath</code> is not supplied
</p>
<p>
spaceId
</p>
<p>
string<br /> ID of the space to run the migration script on<br /> true
</p>
<p>
environmentId<br /> <code>'master'</code><br /> string<br /> ID of the environment within the space to run the<br /> false
</p>
<p>
accessToken
</p>
<p>
string<br /> The access token to use<br /> true
</p>
<p>
yes<br /> false<br /> boolean<br /> Skips any confirmation before applying the migration,script<br /> false
</p>
<p>
retryLimit<br /> 5<br /> number<br /> Number of retries before failure (every subsequent retry will increase the timeout to the previous retry by about 1.5 seconds)<br /> false
</p>
<p>
requestBatchSize<br /> 100<br /> number<br /> Limit for every single request<br /> false
</p>
<p>
headers
</p>
<p>
object<br /> Additional headers to attach to the requests<br /> false
</p>
<h3 dir="auto">
<a rel="nofollow noopener" target="_blank" id="user-content-chaining-vs-object-notation" class="anchor" aria-hidden="true" href="#chaining-vs-object-notation"></a>Chaining vs Object notation
</h3>
<p>
All methods described below can be used in two flavors:
</p>
<ol dir="auto">
<li>
The chained approach:</p> <pre>const author = migration
.createContentType(‘author’) .name(‘Author’) .description(‘Author of blog posts or pages’)
<li>
The object approach:</p> <pre>const author = migration.createContentType('author', {
name: ‘Author’, description: ‘Author of blog posts or pages’ })
<p>
While both approaches work, <strong>it is recommended to use the chained approach</strong> since validation errors will display context information whenever an error is detected, along with a line number. The object notation will lead the validation error to only show the line where the object is described, whereas the chained notation will show precisely where the error is located. </li> </ol>
<h3 dir="auto">
<a rel="nofollow noopener" target="_blank" id="user-content-migration" class="anchor" aria-hidden="true" href="#migration"></a><code>migration</code><br />
</h3>
<p>
The main interface for creating and editing content types and tags.
</p>
<h4 dir="auto">
<a rel="nofollow noopener" target="_blank" id="user-content-createcontenttypeid-opts--contenttype" class="anchor" aria-hidden="true" href="#createcontenttypeid-opts--contenttype"></a><code>createContentType(id[, opts])</code> : <a rel="nofollow noopener" target="_blank" href="#content-type">ContentType</a><br />
</h4>
<p>
Creates a content type with provided <code>id</code> and returns a reference to the newly created content type.<br /> <strong><code>id : string</code></strong> – The ID of the content type.<br /> <strong><code>opts : Object</code></strong> – Content type definition, with the following options:
</p>
<ul dir="auto">
<li>
<strong><code>name : string</code></strong> – Name of the content type.
</li>
<li>
<strong><code>description : string</code></strong> – Description of the content type.
</li>
<li>
<strong><code>displayField : string</code></strong> – 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.
</li>
</ul>
<h4 dir="auto">
<a rel="nofollow noopener" target="_blank" id="user-content-editcontenttypeid-opts--contenttype" class="anchor" aria-hidden="true" href="#editcontenttypeid-opts--contenttype"></a><code>editContentType(id[, opts])</code> : <a rel="nofollow noopener" target="_blank" href="#content-type">ContentType</a><br />
</h4>
<p>
Edits an existing content type of provided <code>id</code> and returns a reference to the content type.<br /> Uses the same options as <a rel="nofollow noopener" target="_blank" href="#createcontenttypeid--string-opts--object--contenttype"><code>createContentType</code></a>.
</p>
<h4 dir="auto">
<a rel="nofollow noopener" target="_blank" id="user-content-deletecontenttypeid" class="anchor" aria-hidden="true" href="#deletecontenttypeid"></a><code>deleteContentType(id)</code><br />
</h4>
<p>
Deletes the content type with the provided id and returns <code>undefined</code>. Note that the content type must not have any entries.
</p>
<h4 dir="auto">
<a rel="nofollow noopener" target="_blank" id="user-content-transformentriesconfig" class="anchor" aria-hidden="true" href="#transformentriesconfig"></a><code>transformEntries(config)</code><br />
</h4>
<p>
For the given content type, transforms all its entries according to the user-provided <code>transformEntryForLocale</code> function. For each entry, the CLI will call this function once per locale in the space, passing in the <code>from</code> fields and the locale as arguments.<br /> The transform function is expected to return an object with the desired target fields. If it returns <code>undefined</code>, this entry locale will be left untouched.<br /> <strong><code>config : Object</code></strong> – Content transformation definition, with the following properties:
</p>
<ul dir="auto">
<li>
<strong><code>contentType : string</code></strong> (required) – Content type ID
</li>
<li>
<strong><code>from : array</code></strong> (required) – Array of the source field IDs
</li>
<li>
<strong><code>to : array</code></strong> (required) – Array of the target field IDs
</li>
<li>
<strong><code>transformEntryForLocale : function (fields, locale): object</code></strong> (required) – Transformation function to be applied.</p> <ul dir="auto">
<li>
<code>fields</code> is an object containing each of the <code>from</code> fields. Each field will contain their current localized values (i.e. <code>fields == {myField: {'en-US': 'my field value'}}</code>)
</li>
<li>
<code>locale</code> one of the locales in the space being transformed<br /> The return value must be an object with the same keys as specified in <code>to</code>. Their values will be written to the respective entry fields for the current locale (i.e. <code>{nameField: 'myNewValue'}</code>). If it returns <code>undefined</code>, this the values for this locale on the entry will be left untouched.
</li>
</ul>
</li>
<li>
<strong><code>shouldPublish : bool | 'preserve'</code></strong> (optional) – Flag that specifies publishing of target entries, <code>preserve</code> will keep current states of the source entries (default <code>'preserve'</code>)
</li>
</ul>
<h5 dir="auto">
<a rel="nofollow noopener" target="_blank" id="user-content-transformentries-example" class="anchor" aria-hidden="true" href="#transformentries-example"></a><code>transformEntries</code> Example
</h5>
<pre>migration.transformEntries({
contentType: ’newsArticle’,
from: [‘author’, ‘authorCity’],
to: [‘byline’],
transformEntryForLocale: function (fromFields, currentLocale) {
if (currentLocale === ‘de-DE’) {
return
}
const newByline = ${fromFields.author[currentLocale]} ${fromFields.authorCity[currentLocale]}
return { byline: newByline }
}
})
<p>
For the complete version, please refer to this example.
</p>
<h4 dir="auto">
<a rel="nofollow noopener" target="_blank" id="user-content-derivelinkedentriesconfig" class="anchor" aria-hidden="true" href="#derivelinkedentriesconfig"></a><code>deriveLinkedEntries(config)</code><br />
</h4>
<p>
For each entry of the given content type (source entry), derives a new entry and sets up a reference to it on the source entry. The content of the new entry is generated by the user-provided <code>deriveEntryForLocale</code> function.<br /> For each source entry, this function will be called as many times as there are locales in the space. Each time, it will be called with the <code>from</code> fields and one of the locales as arguments.<br /> The derive function is expected to return an object with the desired target fields. If it returns…
</p>