Migration error/How to migrate empty fields?

I have a source content type (landing page) that has a hero image, we want to create a new content type for this hero image (called hero image, it will have other fields as well) and have the landing page have a reference to this hero image. I have two questions:

  1. I have some landing pages that may have an empty hero file when I am migrating content how do I skip migrating the landing pages with empty fields? Right now I have a hack to create a UUID doe the identity key and generate dummy data for for the entry, is it possible to have an ‘empty’ reference for the fields with no data?

  2. What is the error I am getting below? I don’t understand what am I doing wrong based on the error message.

Below is the migration script:

module.exports = function (migration) {
// // New hero image content type, this type already exists
// const heroImage = migration.createContentType(‘heroImage’)
// .name(‘Hero Image’)
// .displayField(‘desktopImage’);

// heroImage.createField(‘desktopImage’)
// .type(‘Link’)
// .linkType(‘Asset’)
// .required(true)
// .name(‘desktopImage’);

// heroImage.createField(‘altText’)
// .type(‘Symbol’)
// .required(true)
// .name(‘altText’);

// heroImage.createField(‘tabletImage’)
// .type(‘Link’)
// .linkType(‘Asset’)
// .name(‘tabletImage’);

// heroImage.createField(‘mobileImage’)
// .type(‘Link’)
// .linkType(‘Asset’)
// .name(‘mobileImage’);

// Create a new reference field to the original hero image hero image field
const landingPage = migration.editContentType(‘landingPagePoc’);

landingPage.createField(‘heroImageRef’)
.name(‘Hero Image’)
.type(‘Link’)
.linkType(‘Entry’)
.validations([
{
‘linkContentType’: [‘heroImage’]
}
]);

// Derives hero images based on the existing image entry, and links these back to landing page entries.
migration.deriveLinkedEntries({
// Start from landing page’s hero image field
contentType: ‘landingPagePoc’,
from: [‘heroImage’, ‘header’],
// This is the field we created above, which will hold the link to the derived hero images.
toReferenceField: ‘heroImageRef’,
// The new entries to create are of type ‘heroImage’.
derivedContentType: ‘heroImage’,
//Destination field where to put information to
derivedFields: [‘altText’, ‘desktopImage’],
identityKey: async (from) => {
// Hack for empty fields
return from.heroImage ? from.heroImage[‘en-US’].sys.id : require(‘uuid/v4’)();
},
deriveEntryForLocale: async (from, locale) => {
// Hack for empty fields
return {
altText: from.header ? from.header[locale] : ‘empty image’,
desktopImage: from.heroImage ? from.heroImage[locale] : {}
}
}
});

// Disable the old field for now so editors will not see it.
landingPage.editField(‘heroImage’).disabled(true);
// Move new reference field to the top since the old one was at the top
landingPage.moveField(‘heroImageRef’).toTheTop();
}

When I try to run the above migration I get the error below:

Migration unsuccessful
Batch failed

Error: {“status”:“Conflict”,“message”:“”,“details”:{},“url”:“”}

Error: {“status”:“Conflict”,“message”:“”,“details”:{},“url”:“”}

Error: {“status”:“Conflict”,“message”:“”,“details”:{},“url”:“”}

Error: {“status”:“Conflict”,“message”:“”,“details”:{},“url”:“”}

Please check the errors log for more details: C:\Users\errors-1-1573597422736.log
BatchError: Batch failed
at Task.task (C:\Users\contentful-migration\built\bin\cli.js:139:35)
at processTicksAndRejections (internal/process/task_queues.js:93:5) {
batch: {
intent: EntryDeriveIntent {
type: ‘contentType/deriveEntries’,
meta: [Object],
payload: [Object]
},
requests: [
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object], [Object]
],
validationErrors: ,
runtimeErrors:
},
errors: [
Error: {“status”:“Conflict”,“message”:“”,“details”:{},“url”:“”}
at C:\Users\node_modules\contentful-migration\built\bin\cli.js:134:52
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at async Task.task (C:\Users\node_modules\contentful-migration\built\bin\cli.js:125:29),
Error: {“status”:“Conflict”,“message”:“”,“details”:{},“url”:“”}
at C:\Users\node_modules\contentful-migration\built\bin\cli.js:134:52
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at async Task.task (C:\Users\node_modules\contentful-migration\built\bin\cli.js:125:29),
Error: {“status”:“Conflict”,“message”:“”,“details”:{},“url”:“”}
at C:\Users\node_modules\contentful-migration\built\bin\cli.js:134:52
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at async Task.task (C:\Users\node_modules\contentful-migration\built\bin\cli.js:125:29),
Error: {“status”:“Conflict”,“message”:“”,“details”:{},“url”:“”}
at C:\Users\node_modules\contentful-migration\built\bin\cli.js:134:52
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at async Task.task (C:\Users\node_modules\contentful-migration\built\bin\cli.js:125:29)
],
context: [Object: null prototype] {}
`

Below is my landing page

`
{
“name”: “Landing Page POC”,
“description”: “”,
“displayField”: “header”,
“fields”: [
{
“id”: “heroImage”,
“name”: “Hero Image”,
“type”: “Link”,
“localized”: false,
“required”: false,
“validations”: ,
“disabled”: false,
“omitted”: false,
“linkType”: “Asset”
},
{
“id”: “header”,
“name”: “Header”,
“type”: “Symbol”,
“localized”: false,
“required”: false,
“validations”: ,
“disabled”: false,
“omitted”: false
},
{
“id”: “superHeader”,
“name”: “Super header”,
“type”: “Symbol”,
“localized”: false,
“required”: false,
“validations”: ,
“disabled”: false,
“omitted”: false
},
{
“id”: “subHeader”,
“name”: “Sub Header”,
“type”: “Symbol”,
“localized”: false,
“required”: false,
“validations”: ,
“disabled”: false,
“omitted”: false
},
{
“id”: “body”,
“name”: “Body”,
“type”: “Text”,
“localized”: false,
“required”: false,
“validations”: ,
“disabled”: false,
“omitted”: false
},
{
“id”: “callToAction”,
“name”: “Call to Action”,
“type”: “Link”,
“localized”: false,
“required”: false,
“validations”: ,
“disabled”: false,
“omitted”: false,
“linkType”: “Entry”
},
{
“id”: “testimonials”,
“name”: “Testimonials”,
“type”: “Array”,
“localized”: false,
“required”: false,
“validations”: ,
“disabled”: false,
“omitted”: false,
“items”: {
“type”: “Link”,
“validations”: ,
“linkType”: “Entry”
}
},
{
“id”: “carbonImages”,
“name”: “Carbon Images”,
“type”: “Link”,
“localized”: false,
“required”: false,
“validations”: [
{
“linkContentType”: [
“carbonImpactImages”
]
}
],
“disabled”: false,
“omitted”: false,
“linkType”: “Entry”
},
{
“id”: “ratePlanCode”,
“name”: “Rate Plan Code”,
“type”: “Symbol”,
“localized”: false,
“required”: false,
“validations”: ,
“disabled”: false,
“omitted”: false
},
{
“id”: “heroImageRef”,
“name”: “Hero Image”,
“type”: “Link”,
“localized”: false,
“required”: false,
“validations”: [
{
“linkContentType”: [
“heroImage”
]
}
],
“disabled”: false,
“omitted”: false,
“linkType”: “Entry”
}
],
“sys”: {
“space”: {
“sys”: {
“type”: “Link”,
“linkType”: “Space”,
“id”:
}
},
“id”: “landingPagePoc”,
“type”: “ContentType”,
“createdAt”: “2019-10-04T14:20:22.573Z”,
“updatedAt”: “2019-11-12T22:23:43.031Z”,
“environment”: {
“sys”: {
“id”: “feat-poc-test”,
“type”: “Link”,
“linkType”: “Environment”
}
},
“publishedVersion”: 119,
“publishedAt”: “2019-11-12T22:23:43.031Z”,
“firstPublishedAt”: “2019-10-04T14:20:22.928Z”,
“createdBy”: {
“sys”: {
“type”: “Link”,
“linkType”: “User”,
“id”: “3Mq9Y71gyGZbWO3x01ZgB3”
}
},
“updatedBy”: {
“sys”: {
“type”: “Link”,
“linkType”: “User”,
“id”: “3uOEFAOiUsStkk79sRWn72”
}
},
“publishedCounter”: 60,
“version”: 120,
“publishedBy”: {
“sys”: {
“type”: “Link”,
“linkType”: “User”,
“id”: “3uOEFAOiUsStkk79sRWn72”
}
}
}
}

Below is the new hero image that I want the landing page to refer to:

{
“name”: “HeroImage”,
“description”: “”,
“displayField”: “altText”,
“fields”: [
{
“id”: “desktopImage”,
“name”: “desktopImage”,
“type”: “Link”,
“localized”: false,
“required”: true,
“validations”: ,
“disabled”: false,
“omitted”: false,
“linkType”: “Asset”
},
{
“id”: “altText”,
“name”: “altText”,
“type”: “Symbol”,
“localized”: false,
“required”: false,
“validations”: ,
“disabled”: false,
“omitted”: false
},
{
“id”: “tabletImage”,
“name”: “tabletImage”,
“type”: “Link”,
“localized”: false,
“required”: false,
“validations”: ,
“disabled”: false,
“omitted”: false,
“linkType”: “Asset”
},
{
“id”: “mobileImage”,
“name”: “mobileImage”,
“type”: “Link”,
“localized”: false,
“required”: false,
“validations”: ,
“disabled”: false,
“omitted”: false,
“linkType”: “Asset”
}
],
“sys”: {
“space”: {
“sys”: {
“type”: “Link”,
“linkType”: “Space”,
“id”:
}
},
“id”: “heroImage”,
“type”: “ContentType”,
“createdAt”: “2019-11-05T19:30:48.232Z”,
“updatedAt”: “2019-11-12T21:30:04.432Z”,
“environment”: {
“sys”: {
“id”: “feat-poc-test”,
“type”: “Link”,
“linkType”: “Environment”
}
},
“publishedVersion”: 11,
“publishedAt”: “2019-11-12T21:30:04.432Z”,
“firstPublishedAt”: “2019-11-05T19:30:48.495Z”,
“createdBy”: {
“sys”: {
“type”: “Link”,
“linkType”: “User”,
“id”: “3uOEFAOiUsStkk79sRWn72”
}
},
“updatedBy”: {
“sys”: {
“type”: “Link”,
“linkType”: “User”,
“id”: “3uOEFAOiUsStkk79sRWn72”
}
},
“publishedCounter”: 6,
“version”: 12,
“publishedBy”: {
“sys”: {
“type”: “Link”,
“linkType”: “User”,
“id”: “3uOEFAOiUsStkk79sRWn72”
}
}
}
}

Hi @wallace.silveira,

Welcome to the Contentful Community. :wave:

  1. When you use deriveLinkedEntries and want to skip an entry, you can return undefined in the deriveEntryForLocale method. This could look like this
deriveEntryForLocale: async (from, locale) => {
  // Hack for empty fields
  if (!from.header || !from.heroImage) {
    return
  }

  return {
    altText: from.header[locale],
    desktopImage: from.heroImage[locale]
  }
}

If instead you want to process the entry but not create a reference, you can pass null as the value for that field. Note that this only works when the field is not required.

  1. The error message is indeed not very intuitive, I’ve passed that feedback to our Engineering team. What you’re seeing is the migration script trying to create two entries with the same ID. The migration script uses the output from idenityKey as the new ID for the newly created entry (see docs here). Since you use the ID of an existing entry as the ID, it’s not able to generate the new entry.

I hope this helps :slight_smile: