Code Monkey home page Code Monkey logo

contentful-migration's Introduction

header

Join Contentful Community Slack ย  Join Contentful Community Forum

contentful-migration - content model migration tool

Describe and execute changes to your content model and transform entry content.

This repository is actively maintained ย  MIT License ย  Build Status

NPM ย  NPM downloads ย 

What is Contentful?

Contentful 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.

Table of contents

Core Features

  • Content type
    • Edit Content type
    • Create a Content type
  • Entries
    • Transform Entries for a Given Content type
    • Derives a new entry and sets up a reference to it on the source entry
    • Updates tags on entries for a given Content Type
  • Fields
    • Create a field
    • Edit a field
    • Delete a field
    • Rename a field
    • Change a field's control
    • Reset a field's control
    • Copy a field's control
    • Move field
  • Tags
    • Create a Tag
    • Rename a Tag
    • Delete a Tag

Pre-requisites && Installation

Pre-requisites

  • Node LTS

Installation

npm install contentful-migration

Usage

โ— Usage as CLI

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.

Please have a look at the Contentful CLI migration command documentation to learn more about how to use this as command line tool.

Usage as a library

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))

In your migration description file, export a function that accepts the migration object as its argument. For example:

module.exports = function (migration, context) {
  const dog = migration.createContentType('dog')
  const name = dog.createField('name')
  name.type('Symbol').required(true)
}

You can also pass the function directly. For example:

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))

Documentation & References

Configuration

Name Default Type Description Required
filePath string The path to the migration file if migrationFunction is not supplied
migrationFunction function Specify the migration function directly. See the expected signature. if filePath is not supplied
spaceId string ID of the space to run the migration script on true
environmentId 'master' string ID of the environment within the space to run the false
accessToken string The access token to use true
yes false boolean Skips any confirmation before applying the migration,script false
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
requestBatchSize 100 number Limit for every single request false
headers object Additional headers to attach to the requests false

Chaining vs Object notation

All methods described below can be used in two flavors:

  1. The chained approach:

    const author = migration
      .createContentType('author')
      .name('Author')
      .description('Author of blog posts or pages')
  2. The object approach:

    const author = migration.createContentType('author', {
      name: 'Author',
      description: 'Author of blog posts or pages'
    })

    While both approaches work, it is recommended to use the chained approach 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.

migration

The main interface for creating and editing content types and tags.

createContentType(id[, opts]) : ContentType

Creates a content type with provided id and returns a reference to the newly created content type.

id : string โ€“ The ID of the content type.

opts : Object โ€“ Content type definition, with the following options:

  • name : string โ€“ Name of the content type.
  • description : string โ€“ Description of the content type.
  • 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.

editContentType(id[, opts]) : ContentType

Edits an existing content type of provided id and returns a reference to the content type. Uses the same options as createContentType.

deleteContentType(id)

Deletes the content type with the provided id and returns undefined. Note that the content type must not have any entries.

transformEntries(config)

For the given content type, transforms all its entries according to the user-provided transformEntryForLocale function. For each entry, the CLI will call this function once per locale in the space, passing in the from fields and the locale as arguments. The transform function is expected to return an object with the desired target fields. If it returns undefined, this entry locale will be left untouched.

config : Object โ€“ Content transformation definition, with the following properties:

  • contentType : string (required) โ€“ Content type ID
  • from : array (required) โ€“ Array of the source field IDs
  • to : array (required) โ€“ Array of the target field IDs
  • transformEntryForLocale : function (fields, locale, {id}): object (required) โ€“ Transformation function to be applied.
    • 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'}})
    • locale one of the locales in the space being transformed
    • id id of the current entry in scope
      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.
  • shouldPublish : bool | 'preserve' (optional) โ€“ Flag that specifies publishing of target entries, preserve will keep current states of the source entries (default 'preserve')
transformEntries Example
migration.transformEntries({
  contentType: 'newsArticle',
  from: ['author', 'authorCity'],
  to: ['byline'],
  transformEntryForLocale: function (fromFields, currentLocale, { id }) {
    if (currentLocale === 'de-DE') {
      return
    }
    const newByline = `${fromFields.author[currentLocale]} ${fromFields.authorCity[currentLocale]}`
    return { byline: newByline }
  }
})

For the complete version, please refer to this example.

deriveLinkedEntries(config)

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 deriveEntryForLocale function. 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 from fields and one of the locales as arguments. The derive function is expected to return an object with the desired target fields. If it returns undefined, the new entry will have no values for the current locale.

config : Object โ€“ Entry derivation definition, with the following properties:

  • contentType : string (required) โ€“ Source content type ID

  • derivedContentType : string (required) โ€“ Target content type ID

  • from : array (required) โ€“ Array of the source field IDs

  • toReferenceField : string (required) โ€“ ID of the field on the source content type in which to insert the reference

  • derivedFields : array (required) โ€“ Array of the field IDs on the target content type

  • 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.

    • 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'}})
  • deriveEntryForLocale : function (fields, locale, {id}): object (required) โ€“ Function that generates the field values for the derived entry.

    • 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'}})
    • locale one of the locales in the space being transformed
    • id id of the current entry in scope

    The return value must be an object with the same keys as specified in derivedFields. Their values will be written to the respective new entry fields for the current locale (i.e. {nameField: 'myNewValue'})

  • 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)

deriveLinkedEntries(config) Example
migration.deriveLinkedEntries({
  contentType: 'dog',
  derivedContentType: 'owner',
  from: ['owner'],
  toReferenceField: 'ownerRef',
  derivedFields: ['firstName', 'lastName'],
  identityKey: async (fromFields) => {
    return fromFields.owner['en-US'].toLowerCase().replace(' ', '-')
  },
  shouldPublish: true,
  deriveEntryForLocale: async (inputFields, locale, { id }) => {
    if (locale !== 'en-US') {
      return
    }
    const [firstName, lastName] = inputFields.owner[locale].split(' ')
    return {
      firstName,
      lastName
    }
  }
})

For the complete version of this migration, please refer to this example.

transformEntriesToType(config)

For the given (source) content type, transforms all its entries according to the user-provided transformEntryForLocale function into a new entry of a specific different (target) content type. For each entry, the CLI will call the function transformEntryForLocale once per locale in the space, passing in the from fields and the locale as arguments. The transform function is expected to return an object with the desired target fields. If it returns undefined, this entry locale will be left untouched.

config : Object โ€“ Content transformation definition, with the following properties:

  • sourceContentType : string (required) โ€“ Content type ID of source entries

  • targetContentType : string (required) โ€“ Targeted Content type ID

  • from : array (optional) โ€“ Array of the source field IDs, returns complete list of fields if not configured

  • identityKey: function (fields): string (required) - Function to create a new entry ID for the target entry

  • shouldPublish : bool | 'preserve' (optional) โ€“ Flag that specifies publishing of target entries, preserve will keep current states of the source entries (default false)

  • 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.

  • removeOldEntries : bool (optional) โ€“ Flag that specifies if source entries should be deleted (default false)

  • transformEntryForLocale : function (fields, locale, {id}): object (required) โ€“ Transformation function to be applied.

    • 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'}})
    • locale one of the locales in the space being transformed
    • id id of the current entry in scope

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, the values for this locale on the entry will be left untouched.

transformEntriesToType Example
const MurmurHash3 = require('imurmurhash')

migration.transformEntriesToType({
  sourceContentType: 'dog',
  targetContentType: 'copycat',
  from: ['woofs'],
  shouldPublish: false,
  updateReferences: false,
  removeOldEntries: false,
  identityKey: function (fields) {
    const value = fields.woofs['en-US'].toString()
    return MurmurHash3(value).result().toString()
  },
  transformEntryForLocale: function (fromFields, currentLocale, { id }) {
    return {
      woofs: `copy - ${fromFields.woofs[currentLocale]}`
    }
  }
})

For the complete version of this migration, please refer to this example.

createTag(id[, opts, visibility])

Creates a tag with provided id and returns a reference to the newly created tag.

  • id : string โ€“ The ID of the tag.

  • opts : Object โ€“ Tag definition, with the following options:

    • name : string โ€“ Name of the tag.
  • visibility : 'private' | 'public' Tag visibility - defaults to private.

editTag(id[, opts])

Edits an existing tag of provided id and returns a reference to the tag. Uses the same options as createTag.

deleteTag(id)

Deletes the tag with the provided id and returns undefined. Note that this deletes the tag even if it is still attached to entries or assets.

setTagsForEntries(config)

For the given content type, updates the tags that are attached to its entries according to the user-provided setTagsForEntry function. For each entry, the CLI will call this function once, passing in the from fields, link objects of all tags that already are attached to the entry and link objects of all tags available in the environment. The setTagsForEntry function is expected to return an array with link objects for all tags that are to be added to the entry. If it returns undefined, the entry will be left untouched.

config : Object โ€“ Content transformation definition, with the following properties:

  • contentType : string (required) โ€“ Content type ID
  • from : array (required) โ€“ Array of the source field IDs
  • 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 already attached to the entry. - apiTags is an array containing link objects of all tags available in the environment.
setTagsForEntries Example
migration.createTag('department-sf').name('Department: San Francisco')
migration.createTag('department-ldn').name('Department: London')

const departmentMapping = {
  'san-francisco': 'department-sf',
  london: 'department-ldn'
}

migration.setTagsForEntries({
  contentType: 'news-article',
  from: ['department'],
  setTagsForEntry: (entryFields, entryTags, apiTags) => {
    const departmentField = entryFields.department['en-US']
    const newTag = apiTags.find((tag) => tag.sys.id === departmentMapping[departmentField])

    return [...entryTags, newTag]
  }
})

context

There may be cases where you want to use Contentful API features that are not supported by the migration object. For these cases you have access to the internal configuration of the running migration in a context object.

module.exports = async function (migration, { makeRequest, spaceId, accessToken }) {
  const contentType = await makeRequest({
    method: 'GET',
    url: `/content_types?sys.id[in]=foo`
  })

  const anyOtherTool = new AnyOtherTool({ spaceId, accessToken })
}

makeRequest(config)

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.

config : Object - Configuration for the request based on the Contentful management SDK

  • method : string โ€“ HTTP method
  • url : string - HTTP endpoint
module.exports = async function (migration, { makeRequest }) {
  const contentType = await makeRequest({
    method: 'GET',
    url: `/content_types?sys.id[in]=foo`
  })
}

spaceId : string

The space ID that was set for the current migration.

accessToken : string

The access token that was set for the current migration.

Content type

For a comprehensive guide to content modelling, please refer to this guide.

createField(id[, opts]) : Field

Creates a field with provided id.

id : string โ€“ The ID of the field.

opts : Object โ€“ Field definition, with the following options:

  • name : string (required) โ€“ Field name.

  • type : string (required) โ€“ Field type, amongst the following values:

    • Symbol (Short text)
    • Text (Long text)
    • Integer
    • Number
    • Date
    • Boolean
    • Object
    • Location
    • RichText
    • Array (requires items)
    • Link (requires linkType)
    • ResourceLink (requires allowedResources)
  • items : Object (required for type Array) โ€“ Defines the items of an Array field. Example:

    items: {
      type: 'Link',
      linkType: 'Entry',
      validations: [
        { linkContentType: [ 'my-content-type' ] }
      ]
    }
  • linkType : string (required for type Link) โ€“ Type of the referenced entry. Value must be either Asset or Entry.

  • allowedResources (required for type ResourceLink) - Defines which resources can be linked through the field.

  • required : boolean โ€“ Sets the field as required.

  • validations : Array โ€“ Validations for the field. Example:

    validations: [{ in: ['Web', 'iOS', 'Android'] }]

    See The CMA documentation for the list of available validations.

  • localized : boolean โ€“ Sets the field as localized.

  • disabled : boolean โ€“ Sets the field as disabled, hence not editable by authors.

  • omitted : boolean โ€“ Sets the field as omitted, hence not sent in response.

  • deleted : boolean โ€“ Sets the field as deleted. Requires to have been omitted first. You may prefer using the deleteField method.

  • defaultValue : Object โ€“ Sets the default value for the field. Example:

    defaultValue: {
      "en-US": false,
      "de-DE": true
    }

editField(id[, opts]) : Field

Edits the field of provided id.

id : string โ€“ The ID of the field to edit.

opts : Object โ€“ Same as createField listed above.

deleteField(id) : void

Shorthand method to omit a field, publish its content type, and then delete the field. This implies that associated content for the field will be lost.

id : string โ€“ The ID of the field to delete.

changeFieldId (currentId, newId) : void

Changes the field's ID.

currentId : string โ€“ The current ID of the field.

newId : string โ€“ The new ID for the field.

moveField (id) : MovableField

Move the field (position of the field in the web editor)

id: string - The ID of the field to move

.moveField(id) returns a movable field type which must be called with a direction function:

  • .toTheTop()
  • .toTheBottom()
  • .beforeField(fieldId)
  • .afterField(fieldId)

Example:

module.exports = function (migration) {
  const food = migration.editContentType('food')

  food.createField('calories').type('Number').name('How many calories does it have?')

  food.createField('sugar').type('Number').name('Amount of sugar')

  food.createField('vegan').type('Boolean').name('Vegan friendly')

  food.createField('producer').type('Symbol').name('Food producer')

  food.createField('gmo').type('Boolean').name('Genetically modified food')

  food.moveField('calories').toTheTop()
  food.moveField('sugar').toTheBottom()
  food.moveField('producer').beforeField('vegan')
  food.moveField('gmo').afterField('vegan')
}

changeFieldControl (fieldId, widgetNamespace, widgetId[, settings]) : void

Changes control interface of given field's ID.

fieldId : string โ€“ The ID of the field.

widgetNamespace : string โ€“ The namespace of the widget, one of the following values:

  • builtin (Standard widget)
  • app (Custom App)
  • extension (Custom UI extension)
  • app (Custom app widget)

widgetId : string โ€“ The new widget ID for the field. See the editor interface documentation for a list of available widgets.

settings : Object โ€“ Widget settings and extension instance parameters. Key-value pairs of type (string, number | boolean | string). For builtin widgets, the the following options are available:

  • helpText : string โ€“ This help text will show up below the field.
  • trueLabel : string (only for fields of type boolean) โ€“ Shows this text next to the radio button that sets this value to true. Defaults to โ€œYesโ€.
  • falseLabel : string (only for fields of type boolean) โ€“ Shows this text next to the radio button that sets this value to false. Defaults to โ€œNoโ€.
  • stars : number (only for fields of type rating) โ€“ Number of stars to select from. Defaults to 5.
  • format : string (only for fields of type datePicker) โ€“ One of โ€œdateonlyโ€, โ€œtimeโ€, โ€œtimeZโ€ (default). Specifies whether to show the clock and/or timezone inputs.
  • ampm : string (only for fields of type datePicker) โ€“ Specifies which type of clock to use. Must be one of the strings โ€œ12โ€ or โ€œ24โ€ (default).
  • bulkEditing : boolean (only for fields of type Array) โ€“ Specifies whether bulk editing of linked entries is possible.
  • trackingFieldId : string (only for fields of type slugEditor) โ€“ Specifies the ID of the field that will be used to generate the slug value.
  • showCreateEntityAction : boolean (only for fields of type Link) - specifies whether creation of new entries from the field is enabled.
  • showLinkEntityAction : boolean (only for fields of type Link) - specifies whether linking to existing entries from the field is enabled.

resetFieldControl (fieldId) : void

fieldId : string โ€“ The ID of the field.

copyFieldControl (sourceFieldId, destinationFieldId) : void

sourceFieldId : string โ€“ The ID of the field to copy the control setting from. destinationFieldId : string โ€“ The ID of the field to apply the copied control setting to.

addSidebarWidget (widgetNamespace, widgetId[, settings, insertBeforeWidgetId]) : void

Adds a builtin or custom widget to the sidebar of the content type.

widgetNamespace: string โ€“ The namespace of the widget, one of the following values:

  • sidebar-builtin (Standard widget, default)
  • extension (Custom UI extension)

widgetId : string โ€“ The ID of the builtin or extension widget to add.

settings : Object โ€“ Instance settings for the widget. Key-value pairs of type (string, number | boolean | string)

insertBeforeWidgetId : Object โ€“ Insert widget above this widget in the sidebar. If null, the widget will be added to the end.

updateSidebarWidget (widgetNamespace, widgetId, settings) : void

Updates the configuration of a widget in the sidebar of the content type.

widgetNamespace: string โ€“ The namespace of the widget, one of the following values:

  • sidebar-builtin (Standard widget, default)
  • extension (Custom UI extension)

widgetId : string โ€“ The ID of the builtin or extension widget to add.

settings : Object โ€“ Instance settings for the widget. Key-value pairs of type (string, number | boolean | string)

removeSidebarWidget (widgetNamespace, widgetId) : void

Removes a widget from the sidebar of the content type.

widgetNamespace: string โ€“ The namespace of the widget, one of the following values:

  • sidebar-builtin (Standard widget, default)
  • extension (Custom UI extension)

widgetId : string โ€“ The ID of the builtin or extension widget to remove.

resetSidebarToDefault () : void

Resets the sidebar of the content type to default.

configureEntryEditor (widgetNamespace, widgetId[, settings]) : void

Sets the entry editor to specified widget.

widgetNamespace: string โ€“ The namespace of the widget. widgetId : string โ€“ The ID of the builtin or extension widget to add. settings : Object โ€“ Instance settings for the widget. Key-value pairs of type (string, number | boolean | string). Optional.

configureEntryEditors (EntryEditor[]) : void

As opposed to configureEntryEditor which only sets one editor, this sets a list of editors to the current editor interface of a content-type.

Each EntryEditor has the following properties:

  • widgetNamespace: string โ€“ The namespace of the widget (i.e: app, extension or builtin-editor).
  • widgetId : string โ€“ The ID of the builtin, extension or app widget to add.
  • settings : Object โ€“ Instance settings for the widget. Key-value pairs of type (string, number | boolean | string). Optional.

resetEntryEditorToDefault () : void

Resets the entry editor of the content type to default.

createEditorLayout () : EditorLayout

Creates an empty editor layout for this content type.

editEditorLayout () : EditorLayout

Edits the editor layout for this content type.

deleteEditorLayout () : void

Deletes the editor layout for this content type.

setAnnotations(AnnotationId[])

Configure the annotations assigned to this content type. See annotations documentation for more details on valid AnnotationId.

clearAnnotations()

Remove all assigned annotations from this content type

Field

The field object has the same methods as the properties listed in the ContentType.createField method.

In addition the following methods allow to manage field annotations.

setAnnotations(AnnotationId[])

Configure the annotations assigned to this field. See annotations documentation for more details on valid AnnotationId.

clearAnnotations()

Remove all assigned annotations from this field.

Editor Layout

moveField(id) : MovableEditorLayoutItem

Moves the field with the provided id.

moveField(id) returns a movable editor layout item type which must be called with a direction function:

  • .toTheTopOfFieldGroup(groupId)
      • if no groupId is provided, the field will be moved within its group
  • .toTheBottomOfFieldGroup(groupId)
      • if no groupId is provided, the field will be moved within its group
  • .beforeFieldGroup(groupId)
  • .afterFieldGroup(groupId)
  • .beforeField(fieldId)
  • .afterField(fieldId)

createFieldGroup(id[, opts]) : EditorLayoutFieldGroup

Creates a tab with the provided id.

id : string โ€“ The ID of the group.

opts : Object โ€“ Group settings, with the following options:

  • name : string (required) โ€“ Group name.

deleteFieldGroup (id) : void

Deletes the group with the provided id from the editor layout, 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.

changeFieldGroupId (currentId, newId)

Changes the groupโ€™s ID.

currentId : string โ€“ The current ID of the group.

newId : string โ€“ The new ID for the group.

editFieldGroup (id[, opts]) : EditorLayoutFieldGroup

Editor Layout Field Group

createFieldGroup (id[, opts]) : EditorLayoutFieldGroup

Creates a field set with the provided id.

id : string โ€“ The ID of the group.

opts : Object โ€“ Group settings, with the following options:

  • name : string (required) โ€“ Group name.

changeFieldGroupControl (id, widgetNamespace, widgetId[, settings]) : void

Sets the group control for a field group.

widgetNamespace : string โ€“ The namespace for the group control. Currently allowed: builtin. widgetId : string - The widget ID for the group control. Allowed values: fieldset, topLevelTab. settings : Object โ€“ Field set settings, with the following properties:

  • helpText : string โ€“ Help text for the field set. Displayed when editing.
  • collapsible : boolean โ€“ Whether the field set can be collapsed when editing.
  • collapsedByDefault : string โ€“ Whether the field set is collapsed when opening the editor.

Validation errors

You can learn more from the possible validation errors here.

Example migrations

You can check out the examples to learn more about the migrations DSL. Each example file is prefixed with a sequence number, specifying the order in which you're supposed to run the migrations, as follows:

const runMigration = require('contentful-migration/built/bin/cli').runMigration

const options = {
  spaceId: '<space-id>',
  accessToken: '<access-token>',
  yes: true
}

const migrations = async () => {
  await runMigration({ ...options, ...{ filePath: '01-angry-dog.js' } })
  await runMigration({ ...options, ...{ filePath: '02-friendly-dog.js' } })
  await runMigration({ ...options, ...{ filePath: '03-long-example.js' } })
  await runMigration({ ...options, ...{ filePath: '04-steps-errors.js' } })
  await runMigration({ ...options, ...{ filePath: '05-plan-errors.js' } })
  await runMigration({ ...options, ...{ filePath: '06-delete-field.js' } })
  await runMigration({ ...options, ...{ filePath: '07-display-field.js' } })
}

migrations()

Writing Migrations in Typescript

You can use Typescript to write your migration files using ts-node! First npm install --save ts-node typescript, then run your migration with ts-node:

node_modules/.bin/ts-node node_modules/.bin/contentful-migration -s $CONTENTFUL_SPACE_ID -a $CONTENTFUL_MANAGEMENT_TOKEN my_migration.ts

An example Typescript migration:

import { MigrationFunction } from 'contentful-migration'

// typecast to 'MigrationFunction' to ensure you get type hints in your editor
export = function (migration, { makeRequest, spaceId, accessToken }) {
  const dog = migration.createContentType('dog', {
    name: 'Dog'
  })

  const name = dog.createField('name')
  name.name('Name').type('Symbol').required(true)
} as MigrationFunction

Here's how it looks inside VS Code:

typescript migration in vscode

Troubleshooting

  • Unable to connect to Contentful through your Proxy? Try to set the rawProxy option to true.
runMigration({
  proxy: 'https://cat:[email protected]:1234',
  rawProxy: true,
  ...
})

Updating Integration tests fixtures

  • To add new/update integration tests, you need to set environment variable NOCK_RECORD=1 which should automatically update fixtures

Reach out to us

You have questions about how to use this library?

  • Reach out to our community forum: Contentful Community Forum
  • Jump into our community slack channel: Contentful Community Slack

You found a bug or want to propose a feature?

  • File an issue here on GitHub: File an issue. Make sure to remove any credential from your code before sharing it.

You need to share confidential information or have other questions?

  • File a support ticket at our Contentful Customer Support: File support ticket

Get involved

PRs Welcome

We appreciate any help on our repositories. For more details about how to contribute see our CONTRIBUTING.md document.

License

This repository is published under the MIT license.

Code of Conduct

We want to provide a safe, inclusive, welcoming, and harassment-free space and experience for all participants, regardless of gender identity and expression, sexual orientation, disability, physical appearance, socioeconomic status, body size, ethnicity, nationality, level of experience, age, religion (or lack thereof), or other identity markers.

contentful-migration's People

Contributors

andreascful avatar anho avatar baskiers avatar connor-baer avatar crissto avatar damienxy avatar dependabot-preview[bot] avatar dependabot[bot] avatar dlitvakb avatar electriccode avatar fkiriakos avatar jfr3000 avatar kdamball avatar khaledgarbaya avatar lehnerm avatar madtrick avatar makinwab avatar marcolink avatar matthew-contentful avatar phoebeschmidt avatar roblinde avatar ruderngespra avatar silhoue avatar stefanjudis avatar t-col avatar timbeyer avatar veu avatar vinz93 avatar webstackdev avatar zcei avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

contentful-migration's Issues

Add web app validations to migration cli

When defining short text validations in the CLI it would be great to provide the validations that are available in the web app.

screen shot 2018-04-25 at 14 30 17

Is this something we'd be up for providing?

changeEditorInterface fails when used with editContentType

If I create a new Content Model with a migration like the one below, then changeEditorInterface works and the migration goes through.

module.exports = function(migration) {
    const genricModel = migration.createContentType('genericModel', {
        name: 'Generic test model',
        description: 'Testing the migration API'
    });
    genricModel.createField('region', {
        name: 'Country',
        type: 'Symbol',
        required: true,
        validations: [{'in': ['SE', 'DK', 'NO']}]
    });
    genricModel.changeEditorInterface('region', 'radio');
};

But if I run this migration on an already existing Content Model, the actual field is created but the changeEditorInterface throws an error and the migration halts, but the field has been created with the default editor interface.

module.exports = function(migration) {
    const genricModel = migration.editContentType('genericModel');
    genricModel.createField('region', {
        name: 'Country',
        type: 'Symbol',
        required: true,
        validations: [{'in': ['SE', 'DK', 'NO']}]
    });
    genricModel.changeEditorInterface('region', 'radio');
};

The migration above givs this error

The following migration has been planned

Update Content Type genericModel

  Create field region
    - name: "Country"
    - type: "Symbol"
    - required: true
    - validations: [{"in":["SE","DK","NO"]}]

Publish Content Type genericModel
Update editor interface for Content Type genericModel
  - Field region: radio
? Do you want to apply the migration Yes
 โœ” Update Content Type genericModel
 โฏ Update editor interface for Content Type genericModel
   โœ– Making requests (1/1)
     โ†’ Batch failed
๐Ÿšจ  Migration unsuccessful
Batch failed

Error: {"status":"Conflict","message":"","details":{},"url":"https://api.contentful.com:443/spaces/space-id-anonymized/environments/master/content_types/genericModel/editor_interface"}

and produces the following log, I've removed project path and space id

VersionMismatch: {
  "status": 409,
  "statusText": "Conflict",
  "message": "",
  "details": {},
  "request": {
    "url": "https://api.contentful.com:443/spaces/space-id-anonymized/environments/master/content_types/genericModel/editor_interface",
    "headers": {
      "Accept": "application/json, text/plain, */*",
      "Content-Type": "application/vnd.contentful.management.v1+json",
      "X-Contentful-User-Agent": "app contentful.migration-cli/0.6.0; sdk contentful-management.js/4.1.2; platform node.js/8.9.4; os macOS/17.4.0;",
      "Authorization": "Bearer ...e61aa",
      "user-agent": "node.js/v8.9.4",
      "Accept-Encoding": "gzip",
      "X-Contentful-Version": 15,
      "Content-Length": 144
    },
    "method": "put",
    "payloadData": "{\"controls\":[{\"fieldId\":\"title\",\"widgetId\":\"singleLine\"},{\"fieldId\":\"paragraph\",\"widgetId\":\"markdown\"},{\"fieldId\":\"region\",\"widgetId\":\"radio\"}]}"
  },
  "requestId": "f067c3e7f1013ce64ae867a5741fcf1f"
}
    at errorHandler (/project-path-anonymized/node_modules/contentful-management/dist/contentful-management.node.js:693:15)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)

My workaround at the moment is to run the createField in one migration and then changeEditorInterface in a second.

Migration editContentType with validations fails when a meda field with Accept only specified image dimensions exists

When running a migration on a Content Model that has a media field with the validation option Accept only specified image dimensions the migration fails with errors. When I uncheck Accept only specified image dimensions the migration goes through without errors.

The following migration with the Accept only specified image dimensions checked

module.exports = function(migration) {
    const genricModel = migration.editContentType('genericModel');
    genricModel.createField('region', {
        name: 'Country',
        type: 'Symbol',
        required: true,
        validations: [{'in': ['SE', 'DK', 'NO']}]
    });
};

throws this error

The following migration has been planned

Update Content Type genricModel

  Create field region
    - name: "Country"
    - type: "Symbol"
    - required: true
    - validations: [{"in":["SE","DK","NO"]}]


Error: "max" validation expected to be "number", but got "null"
Error: "max" validation expected to be "number", but got "null"

Publish Content Type genricModel
๐Ÿšจ  Migration unsuccessful

If I uncheck the Accept only specified image dimensions the migration goes through.

Editor Interface support

When creating or editing a content type, is there a way to set the editor interface using this tool?

Undefined environmentId is causing space and access error

When using the migration-cli as library you get the following error message, even if you have filled out both spaceId and accessToken.

The provided space does not exist or you do not have access.

๐Ÿšจ  Migration unsuccessful

Since the clientConfig object to the createManagementClient depends on the environmentId and it is not specified in the options object in the documentation I think a lot of people might be missing what's going on.

I suggest the following solutions.

  1. Add the environmentId to the options object in the documentation.
const options = {
  fielPath: '<migration-file-path>',
  spaceId: '<space-id>',
  environmentId: '<environmentId (default is master)>',
  accessToken: '<access-token>'
}
  1. or change the environmentId declaration to

const environmentId = argv.environmentId || 'master'

Both solution works, but I don't know which one is the best one and there might even be a third or fourth option since this have to do with future updates to Contentful space management.

Your example from the docs doesn't work

Error: The property "name" is required on a content type.
Error: The property "name" is required on the field "name".

For the code from your docs:

const dog = migration.createContentType('dog');
const name = dog.createField('name');
name.type('Symbol').required(true);

Please fix it

Use stderr for errors

Currently all errors are sent to stdout (via console.log), all errors should go to stderr (via console.error)

a simple way to add to a validations array

Need a way to add a new row to a validation.

Currently if i want to add a new entry in the validations array, the code looks like this:

dynamicContentRowType.editField( 'contentEntry', { "name": "Content entry", "type": "Array", "localized": false, "required": true, "validations": [], "disabled": false, "omitted": false, "items": { "type": "Link", "validations": [ { "linkContentType": [ "type0", "type1", "type2", "type3", "type4", "type5", "type6", "type7", ... "type50" //you get the picture! ] } ], "linkType": "Entry" } }

This cumbersome syntax makes it tricky to keep track of what the change actually was and hard to debug. So the ideal scenario would be:
dynamicContentRowType.editField('contentEntry').items.addValidation('type55');

Communicate "Migration Done!" when it wasn't done - from your docs

If you provide wrong path to the file, you get an error, but anyways runMigration resolve the promise successfully, and you get an info, that everything went fine...

{ Error: Cannot find module './migrations/add-dog-ct'
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:581:15)
at Function.Module._load (internal/modules/cjs/loader.js:507:25)
at Module.require (internal/modules/cjs/loader.js:637:17)
at require (internal/modules/cjs/helpers.js:20:18)
at run (/Users/oskarkaminski/Documents/projects/ace-website/node_modules/contentful-migration/built/bin/cli.js:40:29)
at Object. (/Users/oskarkaminski/Documents/projects/ace-website/db/runMigrations.js:10:1)
at Module._compile (internal/modules/cjs/loader.js:689:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
at Module.load (internal/modules/cjs/loader.js:599:32)
at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
at Function.Module._load (internal/modules/cjs/loader.js:530:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
at startup (internal/bootstrap/node.js:279:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:752:3) code: 'MODULE_NOT_FOUND' }
Migration Done!

Token/SpaceId not exists even if they are

Expected Behavior

Run a contentful migration

Actual Behavior

Not able to run contentful migrations (most basic one*), getting this error
The provided space does not exist or you do not have access. Removing contentful-migration@0.10.0 and switching back to 0.9.0 works as expected using the same spaceId and accessToken

*migration example:

module.exports = function(migration) {
    migration.createContentType('test', {
        'name': 'Test',
        'description': 'Test'
    });
}

Environment

  • Node Version: v10.0.0
  • Package Manager Version: 5.6.0
  • Operating System: Darwin Mindits-MacBook-Pro.local 17.6.0 Darwin Kernel Version 17.6.0: Tue May 8 15:22:16 PDT 2018; root:xnu-4570.61.1~1/RELEASE_X86_64 x86_64
  • Package Version: 0.10.0

derivedLinkedEntries requiring identityKey function

I am trying to refactor an existing content model by

  • factoring out some of the fields in the model to a new content type
  • create a new entry of the new content type per existing entry
  • link those two

In order to achieve this, I was using https://github.com/contentful/migration-cli#derivelinkedentriesconfig. The issue I found was that identityKey function was required.

If I use derivelinkedentries like the example, I'd lose the auto-generated entry id like the rest of the entries have. I'm worried that this will lead to the same entry id at some point and the best way to avoid this to rely on https://www.contentful.com/developers/docs/references/content-management-api/#/reference/entries/entries-collection/create-an-entry/console/js. So I believe there should be an option to use auto-generated entry id when creating a new entry from an existing entry.

Catching errors or aborted runs when using migration CLI as a library

Expected Behavior

When use the CLI tool as a library and running a migration, I need to catch both:

  1. When a migration fails
  2. When the user hits n when prompted to proceed with the migration

Actual Behavior

The function currently does not throw an error or return a Promise that would allow me to catch these cases. Therefore, when I run a migration via my Node scripts, subsequent code that should only run on successful migration is running every time.

Possible Solution

The returns should for each case could perhaps be improved:

Steps to Reproduce

Stripped down code:

const cfRunMigration = require("contentful-migration/built/bin/cli").default;

const runMigration = async () => {
  try {
    const options = {
      // redacted
    };

    await cfRunMigration(options);
  } catch (e) {
    // doesn't seem to fire on errors, because no error is thrown by cfRunMigration
    console.log(e);
  }

  // Always runs, even on error or when migration is aborted
  thisShouldNotAlwaysRun();
}

I can sort of solve for the aborted attempts by checking if the output of cfRunMigration is undefined; but that feels dirty:

const cfRunMigration = require("contentful-migration/built/bin/cli").default;

const runMigration = async () => {
  let migrationResult;

  try {
    const options = {
      // redacted
    };

    migrationResult = await cfRunMigration(options);
  } catch (e) {
    // doesn't seem to fire on errors, because no error is thrown by cfRunMigration
    console.log(e);
  }

  // Dirty check
  if (typeof migrationResult !== 'undefined') {
    thisShouldNotAlwaysRun();
  }
}

Context

I am trying to create a process and automated scripts for running migrations against multiple environments on our CI. Our plan is, when running a migration script file, we will persist the file name to the target space/environment as a record, and a mechanism against attempting to re-run the migration file later. We have need to check that a migration has been truly, successfully run (both on our CI and during local development), before persisting the filename to the space/environment. Currently, that's tricky because of the lack of proper returns for error/aborted attempt cases.

Environment

  • Node Version: v8.9.4
  • Package Manager Version: v6.1.0
  • Operating System: Mac OSX 10.13.5
  • Package Version: v0.9.0

Transform Entries should not publish if the source entry is a draft

I'm writing a migration to rename a field in a content type, and doing a transformation to copy the value over. Unfortunately when I do so, the "Transform Entries" step also runs over a draft entry and then tries to publish it. This entry is not yet valid due to other fields that have nothing to do with the migration.

Expected behavior:
The migration should publish the transformation for any entries that are already published. It should not publish drafts.

Actual behavior:

The following migration has been planned

Update Content Type section-hero

  Create field style
    - name: "Style"
    - type: "Symbol"
    - localized: false
    - required: false
    - validations: [{"in":["default","large"]}]
    - disabled: false
    - omitted: false

Publish Content Type section-hero
Transform entries for section-hero
  - from: styles
  - to: style
Update Content Type section-hero

  Delete field styles

Publish Content Type section-hero
Update editor interface for Content Type section-hero

  Update field style
    - widgetId: "dropdown"
    - helpText: "Choose one of the available styles for this Section."
? Do you want to apply the migration Yes
 โœ” Update Content Type section-hero
 โฏ Transform entries for section-hero
   โœ– Making requests (6/6)
     โ†’ Batch failed
   Update Content Type section-hero
   Update editor interface for Content Type section-hero
๐Ÿšจ  Migration unsuccessful
Batch failed

Error: {"status":"Unprocessable Entity","message":"Validation error","details":{"errors":[{"name":"required","path":["fields","text"],"details":"The property \"text\" is required here"}]},"url":"https://api.contentful.com:443/spaces/xxxx/environments/xxxx/entries/xxxx/published"}

Relevant log entry:

InvalidEntry: {
  "status": 422,
  "statusText": "Unprocessable Entity",
  "message": "Validation error",
  "details": {
    "errors": [
      {
        "name": "required",
        "path": [
          "fields",
          "text"
        ],
        "details": "The property \"text\" is required here"
      }
    ]
  },
  "request": {
    "url": "https://api.contentful.com:443/spaces/xxxx/environments/xxxx/entries/xxxx/published",
    "headers": {
      "Accept": "application/json, text/plain, */*",
      "Content-Type": "application/vnd.contentful.management.v1+json",
      "X-Contentful-User-Agent": "app contentful.migration-cli/0.8.0; sdk contentful-management.js/5.0.0-beta2; platform node.js/v8.4.0; os macOS/16.7.0;",
      "Authorization": "Bearer ...",
      "user-agent": "node.js/v8.4.0",
      "Accept-Encoding": "gzip",
      "X-Contentful-Version": 2,
      "X-Contentful-Content-Type": "section-hero"
    },
    "method": "put"
  },
  "requestId": "412632edbef61d38f2f6aaa5b4dca13a"
}
    at errorHandler (./node_modules/contentful-migration/node_modules/contentful-management/dist/webpack:/contentfulManagement/error-handler.js:60:17)
    at <anonymous>
    at process._tickDomainCallback (internal/process/next_tick.js:228:7)

Thanks for all your hard work! Overall it's a great tool.

Allow migrations to handle converting a JSON field into actual content types

The contentful-migration library is powerful and allows many types of flexible migrations.

One recent issue I've come across is that there is no easy way to convert a JSON field into actual content type entries. Specifically a JSON array.

Ex. I have a content type of called 'TagSet' with a JSON field called 'tags'. This JSON structure would look something like:

{ tags: [ { "tag": "github", "alternative":"github.com" }, { "tag": "bitbucket", "alternative":"bitbucket.com" } ] }

I'd like to be able to convert this into a content type 'Tag' with fields 'tag' and 'alternative'. So one TagSet could become 1+ Tags.

I think that the deriveLinkedEntries method could be a good place to start to support this. I know that there was some recent work done in #127 that solved an n to n deriveLinkedEntries migration that seems in line.

This is more of a feature request than a bug (unless there is a way to do this with the existing tools - without getting too deep into custom API work). I filed this after suggetion from Stefan on Slack.

Changing field ID does not work

Expected Behavior

Changing field ID should work

Actual Behavior

The migration is unsuccessful because the ID of the field in the controls does not match.

Possible Solution

modify the ID reference in the controls config upon calling changeFieldId or newId

Steps to Reproduce

use any of the above methods (changeFieldId or newId) in a migration

Context

Modifying an existing field to rename the ID in order to preserve the data that currently exists. Modification in place instead of creating a new field and migrating, then deleting the old field.

Environment

  • Node Version: v10.10.0
  • Package Manager Version: 6.4.1
  • Operating System: Darwin 16.7.0 Darwin Kernel Version 16.7.0: Fri Apr 27 17:59:46 PDT 2018; root:xnu-3789.73.13~1/RELEASE_X86_64 x86_64
  • Package Version: 0.12.1

Upsert content types and fields

I'd like to be able to update spaces that may be partially migrated, such that some content types and fields may already exist in the target space in one form or another. However the create and edit methods exposed by the API are exclusive and validation will fail if the space is in the wrong state. It would be nice to have "upsertContentType" and "upsertField" methods that continue the migration regardless of the whether the type/field already exists.

I tried rolling my own upsert functions with try/catch but that obviously doesn't work since the validator doesn't throw. Is there any way to accomplish this with the current API?

module.exports = {
	upsertContentType(migration, contentTypeId) {
		try {
			return migration.createContentType(contentTypeId);
		} catch (error) {
			return migration.editContentType(contentTypeId);
		}
	},

	upsertField(contentType, fieldId) {
		try {
			return contentType.createField(fieldId);
		} catch (error) {
			return contentType.editField(fieldId);
		}
	},
}

The provided space does not exist or you do not have access.

Expected Behavior

Run a migration js script with node

$ node ./migrate.js

I expected to ask a confirmation to run the migration.

Actual Behavior

The script tells me the space does not exists:

{ makeRequest: [Function: makeRequest],
  application: 'contentful.migration-cli/0.10.0',
  accessToken: <accessToken>,
  spaceId: <spaceId>,
  environmentId: 'develop' }
The provided space does not exist or you do not have access.

๐Ÿšจ  Migration unsuccessful

The object you see is the log of the context.
Anyway, I can definitely confirm that the space exists, I used it before in both environments (master, develop). I tried also hardcoding "master" environmentId, but does not work anyway.

Steps to Reproduce

Here is my scripts:

  • migrate.js
const runMigration = require('contentful-migration/built/bin/cli').default;
const pathJoin = require('path').join;
const options = require('./config/tcom/config.test.json');

function migrate() {
  runMigration({
    spaceId: options['space-id'],
    environmentId: options['environment-id'],
    accessToken: options['access-token'],
    filePath: pathJoin(__dirname, 'migrations/3.13.0.js')
  })
    .then(() => console.log('Migration Done!'))
    .catch((e) => console.error(e));
}

migrate();
  • migrations/3.13.0.js
module.exports = async function (migration, context) {
  const pack = migration.editContentType('pack');

  pack.createField('servicesDescription', {
    name: 'Services Description',
    type: 'Text'
  });

  console.log(context);
};

Environment

  • Node Version: 8.11.3
  • Package Manager Version: 5.6.0
  • Operating System: Windows 10 64-bit
  • Package Version: contentful-migration v0.10.0

Migrating with Typescript (Library Help)

Expected Behavior

Successfully run migrations with typescript.

Actual Behavior

I'm getting q is not defined even after following advice from @ristomatti.

Full Output
$ ./node_modules/.bin/ts-node ./node_modules/.bin/contentful-migration -- -s $CONTENTFUL_SPACE_ID -a $CONTENFUL_ACCESS_TOKEN -e "dev" migrations/create-home-page/rollback.ts

Parses and runs a migration script on a Contentful space.

Usage: contentful-migration [args] <path-to-script-file>

Script: path to a migration script.

Options:
  --version             Show version number                            [boolean]
  --space-id, -s        ID of the space to run the migration script on[required]
  --environment-id, -e  ID of the environment within the space to run the
                        migration script on                  [default: "master"]
  --access-token, -a    The access token to use
                        This takes precedence over environment variables or
                        .contentfulrc
  --proxy               Proxy configuration in HTTP auth format:
                        [http|https]://host:port or
                        [http|https]://user:password@host:port          [string]
  --raw-proxy           Pass proxy config to Axios instead of creating a custom
                        httpsAgent                    [boolean] [default: false]
  --yes, -y             Skips any confirmation before applying the migration
                        script                        [boolean] [default: false]
  -h, --help            Show help                                      [boolean]

Examples:
  contentful-migration  --space-id abcedef my-migration.js

q is not defined

Possible Solution

I was told to add -- before the arguments but that did not work. I've also it can be used as a library but I'm finding the README lacking in that area.

How do you create a library? I have a file with the runMigration function, but how do you execute it? I've tried a few things with no success.

Steps to Reproduce

  1. Create migration script with Typescript
  2. Run ts-node contentful-migration script against newly created typescript file

Context

I love Typescript! I realize it's a brand new feature so there may be some missing pieces. I'm trying to automate the process with CI ๐Ÿ˜„

Environment

  • Node Version: v10.10.0
  • Package Manager Version: Yarn v1.9.4
  • Operating System: Darwin {name and address} Darwin Kernel Version 17.7.0: Thu Jun 21 22:53:14 PDT 2018; root:xnu-4570.71.2~1/RELEASE_X86_64 x86_64
  • Package Version: v0.12.2

Using Management API inside migration scripts

There needs to be a way to use Management API from within migration scripts.
Let's say we have a migration script as follows:

module.exports = function(migration) { const robotsType = migration.createContentType('robots').name('Robots').description('robots'); }

Let's say we want to do something that the migration API doesn't support, like adding a default entry.

If we were to have a contentful management object inside of this script, we would need a way to pass it the space ID and the management token. So ideally we would either have (space id, token) pair passed in or the pre-initialized contentful space object accessible from the args.

Support field appearance

Would be excellent to be able to set the appearance on a field type to one of the default appearance types for that content type -- ie dropdown/radio/single-line for Symbol.

"migrationCreator is not a function" when using contentful-migration "runMigration"

Expected Behavior

-The contentful-migration library should work as described in readme file.
-The runMigration command should run the multiple migration scripts.

Actual Behavior

`mbpus0477hsra18:<DIR> <USUR_NAME>$ contentful space migration --space-id <SPACE_ID> scripts/contentful-migrations/migration_runner.js 

TypeError: migrationCreator is not a function
    at /Users/<USUR_NAME>/.nvm/versions/node/v9.11.1/lib/node_modules/contentful-cli/node_modules/contentful-migration/built/lib/migration-steps/index.js:139:16
    at tryCatcher (/Users/<USUR_NAME>/.nvm/versions/node/v9.11.1/lib/node_modules/contentful-cli/node_modules/bluebird/js/release/util.js:16:23)
    at Function.Promise.attempt.Promise.try (/Users/<USUR_NAME>/.nvm/versions/node/v9.11.1/lib/node_modules/contentful-cli/node_modules/bluebird/js/release/method.js:39:29)
    at Object.migration (/Users/<USUR_NAME>/.nvm/versions/node/v9.11.1/lib/node_modules/contentful-cli/node_modules/contentful-migration/built/lib/migration-steps/index.js:138:23)
    at migration (/Users/<USUR_NAME>/.nvm/versions/node/v9.11.1/lib/node_modules/contentful-cli/node_modules/contentful-migration/built/lib/migration-parser.js:43:49)
    at run (/Users/<USUR_NAME>/.nvm/versions/node/v9.11.1/lib/node_modules/contentful-cli/node_modules/contentful-migration/built/bin/cli.js:53:29)
    at exports.migration (/Users/<USUR_NAME>/.nvm/versions/node/v9.11.1/lib/node_modules/contentful-cli/dist/cmds/space_cmds/migration.js:82:28)
    at <anonymous>
`
const runMigration = require('contentful-migration/built/bin/cli').default;
const path = require('path');

const options = {
  spaceId: '<SPACE_ID>',
  accessToken:
    '<TOKEN>',
  yes: true,
};

const migrations = async () => {
  await runMigration({
    ...options,
    ...{
      filePath: path.join(__dirname, '<FILE_PATH>'),
    },
  });

  await runMigration({
    ...options,
    ...{ filePath: path.join(__dirname, '<FILE_PATH>') },
  });

  await runMigration({
    ...options,
    ...{ filePath: path.join(__dirname, '<FILE_PATH>') },
  });

  await runMigration({
    ...options,
    ...{ filePath: path.join(__dirname, '<FILE_PATH>') },
  });
};

migrations();

Steps to Reproduce

  1. Run the script in the console

Context

I am unable to run the migration scripts in order. The error that is provided is not very helpful and seems to be an internal application error.

Environment

  • Node Version: 9.11.1
  • Package Manager Version: 5.6
  • Operating System: MAC OS 10.13.5
  • Package Version: "contentful-migration": "^0.10.0",

Failed or aborted migration does not exit with non zero exit code

Expected Behavior

Failing or aborted migration exits with a non zero exit code

Actual Behavior

Failing or aborted migration exits with exit code 0

Possible Solution

To make sure failed migrations result in a non zero exit code

throw an error at:

Catch this error and convert it to a non zero exit code at:

await renderPlan(batches, argv.environmentId)

To make sure aborted migrations result in a non zero exit code

Throw a non zero exit code at:

console.warn(chalk`โš ๏ธ {bold.yellow Migration aborted}`)

We could make a Pull request for this if you want?

Steps to Reproduce

Run a migration conaining errors (a typo for exampl) OR abort a migration and display the exit code (echo $?)

Context

Since Contentful CLI is not able to keep track of which migrations have already been executed we've created some scripts to do this. These scripts use the exit code to determine whether a migration has been successfully executed. For now we worked around the issue by checking for the strings Error and aborted in the CLI output.

Environment

I left out other details since the bug is pretty obvious
Contentful CLI 0.14.2

TypeError: migrationCreator is not a function

Running the below command caused a TypeError
contentful-migration --space-id 3mu3dzx76r6a migrations/change-country-field.js

contentful-migration --version => 0.9.0

TypeError: migrationCreator is not a function
    at /usr/local/lib/node_modules/contentful-migration/built/lib/migration-steps/index.js:139:16
    at tryCatcher (/usr/local/lib/node_modules/contentful-migration/node_modules/bluebird/js/release/util.js:16:23)
    at Function.Promise.attempt.Promise.try (/usr/local/lib/node_modules/contentful-migration/node_modules/bluebird/js/release/method.js:39:29)
    at Object.migration (/usr/local/lib/node_modules/contentful-migration/built/lib/migration-steps/index.js:138:23)
    at migration (/usr/local/lib/node_modules/contentful-migration/built/lib/migration-parser.js:43:49)
    at run (/usr/local/lib/node_modules/contentful-migration/built/bin/cli.js:53:29)
    at Object.<anonymous> (/usr/local/lib/node_modules/contentful-migration/bin/contentful-migration:6:1)
    at Module._compile (module.js:643:30)
    at Object.Module._extensions..js (module.js:654:10)
    at Module.load (module.js:556:32)
    at tryModuleLoad (module.js:499:12)
    at Function.Module._load (module.js:491:3)
    at Function.Module.runMain (module.js:684:10)
    at startup (bootstrap_node.js:187:16)
    at bootstrap_node.js:608:3

add version info and support reading from stdin

It would be useful if the tool's current version was displayed in the help text and/or via a -v or --version command line param

also as a command line tool IMO it should be able to accept the migration script from stdin. I have a tool that generates migration scripts and would like to pipe the output of that directly into contentful-migration

Thanks!

Validation with custom message

If I define a field with an asset validation

module.exports = function (migration) {
	const application = migration.createContentType('application')
		.name('Application')
		.displayField('displayName');

	application.createField('avatar')
		.name('Avatar')
		.type('Link')
		.linkType('Asset')
		.localized(true)
		.validations([
			{
				"linkMimetypeGroup": [
					"image"
				]
			},
			{
				"assetFileSize": {
					"max": 19922944
				},
				"message": "Error Message: The file size of your image is to large. The max file size is 19MB."
			}
		]);
};

plan validation fails with

Validation failed

The following migration has been planned but cannot be run because it contains errors

Update Content Type application
  - name: Application
  - displayField: displayName

  Update field avatar
    - name: "Avatar"
    - type: "Link"
    - linkType: "Asset"
    - localized: true
    - validations: [{"linkMimetypeGroup":["image"]},{"assetFileSize":{"max":19922944},"message":"Error Message: The file size of your image is to large. The max file size is 19MB."}]


Error: A field can't have "assetFileSize" as a validation.

๐Ÿšจ  Migration unsuccessful

If I remove the "message" property, the migration plan is accepted and "assetFileSize" appears correctly within the target space.

Creating a field with type Location

The migration:

const event = migration.editContentType('event')
  event.createField('location')
    .type('Location')
    .name('Location')

fails with the error Error: The property "type" on the field "location" must be one of ["Symbol", "Text", "Integer", "Number", "Date", "Boolean", "Object", "Link", "Array"].

In searching the repo, it appears that Location is not currently a type accounted for, though it is in the readme.

changeEditorInterface is not a valid property name for a content type.

When I run the following example migration

contentful-migration --space-id SPACEID 16-change-editor-interface.js

I get the this error

Errors in migration-cli-master/examples/16-change-editor-interface.js
Line 12: "changeEditorInterface" is not a valid property name for a content type.
10:     required: true
11:   });
12:   blogPost.changeEditorInterface('slug', 'slugEditor');
13: };

Editor interface for UI-Extensions is deleted when you run another migration

The first time you run the migration tool for attaching your own ui-extension to a field, everything is working well. The appearance tab for this field shows the ui-extension attached/marked well. The problem is when you run the migration tool another time with another script in the same content type, then the editor interface settings is removed. If you go back to the appearance tab for that field, the ui-extension is not selected well.

Regards!

Suggested changes to Usage as a library section in README

  • The require should have .default as suffix to require the cli function properly or it will return Error: Cannot find module 'contentful-migration-cli/built/bin/cli'
  • filePath option is misspelled, it says fielPath and that the file path works best when using an absolute path.
  • The migration cli needs to be installed locally in the application or script folder node_modules directory. Which might be obvious, but since the main instruction is to install the migration cli globally it can be a bit misleading or cause confusion.

I suggest something like the following or similar.

Usage as a library

Unless you have exported your NODE_PATH you need to have the contenful-migration-cli package installed locally in your application or script directory. Make sure your is relative to your file of execution.

const path = require('path');
const runMigration = require('contentful-migration-cli/built/bin/cli').default
const options = {
  filePath: path.join(__dirname, '<migration-file-path>'),
  spaceId: '<space-id>',
  accessToken: '<access-token>'
}
runMigration(options)

[Bug] Migration fails when content model has existing regex validations without flags

I have a straightforward migration I'm trying to run to add a field to a content type.

module.exports = function (migration) {
     const page = migration.editContentType('article');

    page.createField('stickyCTANavBar')
      .type('Link')
      .linkType('Entry')
      .name('Sticky CTA Nav Bar')
      .validations([{ linkContentType: ['ctaConfig'] }])
 };

The output when I run it:

Publish Content Type article
Update Content Type financialAdvisorLandingPage

  Create field stickyCTANavBar
    - type: "Link"
    - linkType: "Entry"
    - name: "Sticky CTA Nav Bar"
    - validations: [{"linkContentType":["ctaConfig"]},{"flags":""}]


Error: "flags" validation expected to be "string", but got "null"

I have tried removing the content type validation, but I still get this error. Parsing through the documentation, I see there is a flag parameter for regex validations, but I'm not doing any regex validations on this field. If there is something wrong with the content model's other fields, it is not noticeable from the Contentful UI, since the model seems to be valid and working as expected. Any ideas?

Newly created content types don't appear to be saved

I'm running a batch of createContentType scripts.

After they have run (successfully) if I go into Contentful admin they don't appear to be saved - UI prompts to save when clicking away from content type.

I've noticed this means that the slug behaviour does not work (perhaps other things too....). Once the content type has been saved (without any amendment) the slug behaviour works ok.

Grab attached with unsaved state on the Save button.

image

Support field help text

It would be great to be able to set help text on a field in migrations. Documentation and a code search suggests this is not currently possible.

Provide "real" programmatic way to use this library

First, thanks a lot for creating this library!

I'd like to keep track of which migrations were already executed, so that someone doesn't accidentally execute the same migration twice. For this I want to use https://github.com/tj/node-migrate. In the Programmatic Usage section it still says to use the CLI, but with --yes option. It means that I'd have to run it as a system command, instead of simply calling a library function.

Other Contentful libraries like contentful-import and contentful-export provide programmatic usage, so are there any plans to add it to this library as well? I'm not sure if if it would work, but maybe it would be enough to change run function in /src/bin/cli.ts to accept options param, move it to another file that would export it and then update /src/bin/cli.ts to import it and call it with argv argument.

Cannot create validation with assetImageDimension values that are null.

Hello, I have an asset as a field in one of my content models, when I created the validations for it using the website it worked fine and this is the JSON representation it creates:

{
  "assetImageDimensions": {
    "width": {
      "min": 1100,
      "max": null
    },
    "height": {
      "min": null,
      "max": null
    }
  }
}

However, if I try to create a migration using this package and I use null as a max or min value then the cli command errors and doesn't allow me to run the migration:

Error: "max" validation expected to be "number", but got "null"
Error: "min" validation expected to be "number", but got "null"
Error: "max" validation expected to be "number", but got "null"

If I simply omit it the null values like this:

{
  assetImageDimensions: {
    width: {
      min: 1100
    }
  }
}

Then the migration command runs successfully, but when I use the website I cannot edit the field, if I try I get an error "The application has encountered a problem and needs to reload". I am speculating that this is because of the missing height/width or max/min values, the website needs them to be null, however I don't think it's entirely a problem with the website because clearly null values are allowed and the cli command erroneously prevents them from being specified.

Feature request: Include TypeScript .d.ts file with the npm package

Expected Behavior

As the project is written in TypeScript, I would expect it to also allow using the tool with it's typings and to benefit from the defined types when writing migration scripts.

Actual Behavior

The npm package includes only the transpiled code so no types are available.

Possible Solution

Create a separate .d.ts file for the API calls used in the documentation and include it in the npm package.

Error: "changeEditorInterface" is not a valid property name for a content type.

Hi,

I'm unable to set editor interface via the changeEditorInterface method. Here's my code that adds a new field to an existing content model and attempts to change the editor interface for that field:

module.exports = function(migration) {

  const theme = migration.editContentType("theme");

  theme
    .createField("iabTier2")
    .name("IAB Tier 2")
    .type("Symbol")
    .localized(false)
    .required(false)
    .validations([
      {
        in: [
          "Action and Adventure Movies",
          "Adult Album Alternative",
          "Adult Contemporary Music"
        ],
        message: "Invalid value; not a predefined IAB Tier 2 category."
      }
    ])
    .disabled(false)
    .omitted(false);

  theme.moveField("iabTier2").afterField("iabTier1");
  theme.changeEditorInterface("iabTier2", "dropdown");

};

When I attempt to execute my migration script, I get the following error message:

"changeEditorInterface" is not a valid property name for a content type.

I have tried executing example scripts (1, 2) and they both return the same error message.

My NPM version is 6.0.0. and contentful-migration-cli version is 0.7.0 on Mac OS X 10.10.5.

Any help would be greatly appreciated. ๐Ÿ˜„

Migration unsuccessful with transformEntries because "Cannot edit archived" and "Cannot publish archived"

I'm trying to do a migration which create a fields and then transform entries creating values for the new field created before.

Expected Behavior

Run the migration, create the field, fill it with the values I create.

Actual Behavior

Field is created correctly, values are generated correctly, but the migration fails because it try to publish an entry which is in status archived.

$ node migrate.js --portal tcom --env test --migration migrations/3.13.0.js
The following migration has been planned

Update Content Type pack

  Create field servicesDescription
    - name: "Services Description"
    - type: "Text"

Publish Content Type pack
Transform entries for pack
  - from: services
  - to: servicesDescription
? Do you want to apply the migration Yes
 โˆš Update Content Type pack
 > Transform entries for pack
   ร— Making requests (48/48)
     โ†’ Batch failed
๐Ÿšจ  Migration unsuccessful
Batch failed

Error: {"status":"Bad Request","message":"Cannot edit archived","details":{},"url":"https://api.contentful.com:443/spaces/fzchbmaavok4\\environments\\develop\\entries\\2iqxnK1F1aakq24U4C8CSq"}


Error: {"status":"Bad Request","message":"Cannot publish archived","details":{},"url":"https://api.contentful.com:443/spaces/fzchbmaavok4\\environments\\develop\\entries\\2iqxnK1F1aakq24U4C8CSq\\published"}

I have no control on the entries status with the transformEntries migration function. I just provide the transformEntryForLocale function.
So, how can I run this migration skipping the archived entries?
It actually works for the entries that are not archived but the error message is shown and it says "migration unsuccessful", even if it is just partially unsuccessful.

Possible Solution

Parameters on the transformEntries function for choose entries status or just skip the archived entries and return a success message.

Steps to Reproduce

Run any migration with transformEntries function where there are some archived entries.

Environment

  • Node Version: 8.11.3
  • Package Manager Version: 5.6.0
  • Operating System: Windows 10 64-bit
  • Package Version: 0.10.0

"Expected parameter accessToken" error when chaining migrations

I'm trying to use https://github.com/tj/node-migrate to run migrations for multiple spaces. However, when I try to chain migrations, I'm getting this error after the first migration is executed:

(node:28850) UnhandledPromiseRejectionWarning: TypeError: Expected parameter accessToken
    at createClient (/Users/.../node_modules/contentful-migration/node_modules/contentful-management/dist/contentful-management.node.js:2541:11)
    at Object.createManagementClient (/Users/.../node_modules/contentful-migration/built/bin/lib/contentful-client/index.js:47:12)
    at run (/Users/.../node_modules/contentful-migration/built/bin/cli.js:42:40)
    ...

Here's a simplified version of the code I'm trying to run:

const migrate = require('contentful-migration/built/bin/cli').default;

const options = {
  filePath,
  environmentId: 'development',
  yes: true,
};

migrate({ ...options, spaceId: 'some-id' })
  .then(() => migrate({ ...options, spaceId: 'some-other-id' }))
  .then(() => {
    console.log('Finished all migrations');
  });

The first migration completes successfully and then this error is thrown.

I've got my cmaToken stored in ~/.contentfulrc.json file, so I don't pass accessToken with each call.

Missing `Entry title` support for custom fields

Let's take this basic migration:

module.exports = function (migration) {
  const person = migration.createContentType('person', {
    name: 'Person',
    description: 'A content type for a person'
  });

  person.createField('title', {
    name: 'Title',
    type: 'Symbol',
    required: true
  });

  person.createField('age', {
    name: 'Age',
    type: 'Number',
    required: true
  });
};

There is no way to mark a field as Entry title which results in having all fields named Untitled. You can see the exactly behaviour http://sendvid.com/91p42rsz

deriveLinkedEntries does not work with long text field values

We are using the deriveLinkedEntries function to migrate some text onto a separate content type. The field that we want to move is defined as long text, and the content is around 7400 characters.

Our migration script looks like this:

module.exports = (migration) => {
migration.deriveLinkedEntries({
contentType: 'foo',
derivedContentType: 'valuePlanAgreement',
from: ['valuePlanAgreement'],
toReferenceField: 'valuePlanAgreementReference',
derivedFields: ['name', 'valuePlanAgreement'],
identityKey: (fields) => {
if (!fields.valuePlanAgreement) {
return undefined;
}
return fields.valuePlanAgreement['en-US'];
},
deriveEntryForLocale: (fields, locale) => {
if (locale !== 'en-US' || !fields.valuePlanAgreement) {
return undefined;
}
return {
name: '2-year ValuePlan Agreement',
valuePlanAgreement: fields.valuePlanAgreement['en-US']
};
}
});

Expected Behavior

This should migrate the data successfully.

Actual Behavior

When we run this migration, it fails and the following shows in the log file:

414 Request-URI Too Large: {
"status": 414,
"statusText": "Request-URI Too Large",
"message": "",
"details": {},

Attached is a log file showing this occurring (the original data has been swapped out for dummy data).

errors-038.create-value-plan-agreement-entries.migrate-1530196839893.log

Possible Solution

Use a shorter url?

Steps to Reproduce

Use the derivedLinkedEntries function on a field with a large text value (eg. greater than 7000 characters).

Context

We may not be able to use the migration library for this step in our migration. We may have to do this using the management API.

Environment

  • Node Version: v8.10.0
  • Package Manager Version: 5.6.0
  • Operating System: Windows 10
  • Package Version: 0.9.0

Use deriveLinkedEntries with many references field needs documentation.

Expected Behavior

When converting a single field to a many references field, one entry should be created and linked

Actual Behavior

Migration plan looks successful:

The following migration has been planned

Derive entries from articleContactBlock
  - from: emailAddress
  - to: address
  - via: emailAddresses
Update Content Type articleContactBlock

  Update field emailAddresses
    - disabled: false

Publish Content Type articleContactBlock
? Do you want to apply the migration Yes

Migration then fails:

 โฏ Derive entries from articleContactBlock
   โœ– Making requests (28/28)
   
   Update Content Type articleContactBlock
๐Ÿšจ  Migration unsuccessful
Batch failed

Error: {"status":"Unprocessable Entity","message":"Validation error","details":{"errors":[{"name":"type","value":{"sys":{"type":"Link","linkType":"Entry","id":"63dbf7d9-d48f-4aa0-98f9-bacaf6934a80"}},"type":"Array","details":"The type of \"value\" is incorrect, expected type: Array","path":["fields","emailAddresses","nl-NL"]}]},"url":"https://api.contentful.com:443/spaces/ea7q533w75xs/environments/migrate-test/entries/N7JP7I1Qwm8EAmesSQ8We"}

Error: {"status":"Conflict","message":"","details":{},"url":"https://api.contentful.com:443/spaces/ea7q533w75xs/environments/migrate-test/entries/N7JP7I1Qwm8EAmesSQ8We/published"}

....

Possible Solution

Create a variant of the derive entry example which shows how to use it with a many references field

Steps to Reproduce

run a migration similar to this:

module.exports = function (migration) {
    const emailAddress = migration.createContentType('emailAddress').name('Email adres');
    emailAddress.displayField('address');
    emailAddress.createField('address').type('Symbol').name('Adres').validations([{ unique: true }]);
    emailAddress.createField('label').type('Symbol').name('Label').validations([{ unique: true }]);
    emailAddress.createField('description').type('Symbol').name('Titel').validations([{ unique: true }]);
    emailAddress.createField('internal').type('Boolean').name('Voor intern gebruik');

    const articleContactBlock = migration.editContentType('articleContactBlock');

    articleContactBlock.editField('emailAddress').disabled(true);

    articleContactBlock.createField('emailAddresses')
        .name('Email adressen')
        .type('Array')
        .disabled(true)
        .items({
            type: 'Link',
            linkType: 'Entry',
            validations: [
                {
                    'linkContentType': [
                        'emailAddress'
                    ]
                }
            ]
        });

    articleContactBlock.moveField('emailAddresses').afterField('emailAddress');

    const sha1 = require('sha1');

    const articleContactBlock = migration.editContentType('articleContactBlock');
    migration.deriveLinkedEntries({
        contentType: 'articleContactBlock',
        derivedContentType: 'emailAddress',
        from: ['emailAddress'],
        toReferenceField: 'emailAddresses',
        derivedFields: [
            'address',
            'label',
            'description',
            'internal'
        ],
        identityKey: async (fromFields) => {
            if (typeof fromFields.emailAddress === 'undefined') {
                return 'empty';
            }

            return 'sha1' + sha1(fromFields.emailAddress);
        },
        shouldPublish: false,
        deriveEntryForLocale: async (inputFields, locale) => {
            if (typeof inputFields.emailAddress === 'undefined') {
                return {
                    'address': '[email protected]',
                    'label': '',
                    'description': '',
                    'internal': false
                }
            }

            return {
                'address': inputFields.emailAddress[locale],
                'label': '',
                'description': '',
                'internal': false
            };
        }
    });

    articleContactBlock.editField('emailAddresses').disabled(false);

    articleContactBlock.deleteField('emailAddress');
};

Context

We want to allow multiple entries instead of just one and are therefore trying to convert an existing field to many references.

Environment

  • Node Version: v9.11.2
  • Package Manager Version: yarn 1.5.1
  • Operating System: Linux 10a2d3449700 4.9.93-linuxkit-aufs
  • Package Version: Contentful CLI 0.16.1

derivedLinkedEntries should throw if the entity key doesn't match the resourceId regular expression

Expected Behavior

Creating entries with commas, spaces or illegal characters should throw.

Actual Behavior

Creating entries with commas, spaces, or illegal characters in the identity key is allowed.

Possible Solution

Verify the identity key with /^[a-zA-Z0-9\.-_]{1,64}$/ before creating.

Steps to Reproduce

module.exports = function(migration) {
  migration.deriveLinkedEntries({
    contentType: 'dog',
    derivedContentType: 'owner',
    from: ['owner'],
    toReferenceField: 'ownerRef',
    derivedFields: ['firstName', 'lastName'],
    identityKey: (fields) => {
      const [firstName, lastName] = fields.owner.split(' ');
      return `${lastName}, ${firstName}`
    },
    deriveEntryForLocale: async (fields, locale) => {
      /* ... */
    },
  });
};

Context

I was running a migration and the links were failing.

Environment

  • Node Version: 8.9.4

  • Package Manager Version: 5.6.0

  • Operating System: Linux 4.17.3-1-ARCH #1 SMP PREEMPT Tue Jun 26 04:42:36 UTC 2018 x86_64 GNU/Linux

  • Package Version: 0.9.0

Enable bulk editing

It would be useful if bulk editing flag could be set as part of the content model definition.

At present I can't see a way to do this.

thanks!

Cannot edit archived

Expected Behavior

Migration should edit or skip archived entries

Actual Behavior

Migration fails with "status":"Bad Request" and "message":"Cannot edit archived"

Possible Solution

Just skip archived entries

Context

Doing a transformEntryForLocale (copying content from one locale to another)

--yes flag doesn't work if it's not the last arg

SUCCEEDED:
contentful-migration --space-id=<id> --access-token=<token> migrationfile.js --yes
FAILED:
contentful-migration --space-id=<id> --access-token=<token> --yes migrationfile.js
displays the error:

Usage: contentful-migration [args]
Script: path to a migration script.
Options:
--space-id, -s ID of the space to run the migration script on [required]
--access-token, -a The access token to use
This takes precedence over environment variables or
.contentfulrc
--yes, -y Skips any confirmation before applying the migration
script [default: false]
-h, --help Show help [boolean]

Examples:
contentful-migration --space-id abcedef my-migration.js

Please provide the file containing the migration script.

This is confusing and documentation makes it look like the content file is the last expected argument.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.