Introduction

This page describes a way to export page data and file metadata from Drupal CMS 8 to the Xillio UDM (MongoDB).

Prerequisites

To be able to use this connector to your advantage, you will need:

  • Installed software:
    • Xill IDE 3.1
    • MongoDB and a program like RoboMongo
    • The REST module for Drupal
  • To use this demo connector on the Drupal installation you need to give anonymous users full access to the REST API (do not ever do this in a production environment obviously)
  • The demo expects two content types in the Drupal installation:
    • "Document" with the fields file, industry and document_type.
    • "Article" which is the standard Drupal article with a title and a body.
  • Knowledge/experience:
    • Xill
    • JSON

Unified Data Model (UDM) setup

Set up the UDM configuration with the standard decorators and the three custom decorators migration, user and web as follows:

  

use Mongo, ContentType;

include com.xillio.contenttypes.StandardDecorators;
include Settings;

//-------------------------------------------------------------------------
//						  SETUP
//-------------------------------------------------------------------------

var migration = {
     "sourceSystem" : {
          "type" : "STRING",
          "required" : false
     },
     
     "targetSystem" : {
         "type" : "STRING",
         "required" : false
     },
     
     "action" : {
          "type" : "STRING",
          "required" : false
     },
     
     "timestamp" : {
          "type" : "DATE",
          "required" : false
     }
};

var user = {
    "id" : {
        "type" : "NUMBER",
        "required" : true
    },
    
    "firstName" : {
        "type" : "STRING",
        "required" : false
    },
    
    "lastName" : {
        "type" : "STRING",
        "required" : false
    },
    
    "name" : {
        "type" : "STRING",
        "required" : true
    },
    
    "groupId" : {
        "type" : "STRING",
        "required" : false
    },
    
    "email": {
        "type" : "STRING",
        "required" : false
    }
};

var web = {
    "content" : {
        "type" : "LIST",
        "required" : true
    }
};

var custom = {
    "industry" : {
        "type" : "STRING",
        "required" : true
    },
    "document_type" : {
        "type" : "STRING",
        "required" : true
    }
};

//-------------------------------------------------------------------------
//						  MAIN
//-------------------------------------------------------------------------

var settings = getSettings();
var identity = settings.identity;
var database = getConnection(settings.dbName);
var decorators = getStandardDecorators(settings.identity);

registerContentTypes(identity, database);
createIndexes(database);

//-------------------------------------------------------------------------
//						  FUNCTIONS
//-------------------------------------------------------------------------


function registerContentTypes(identity, database) {
    ContentType.decorator("migration", migration, identity);
    ContentType.decorator("user", user, identity);
    ContentType.decorator("web", web, identity);
    ContentType.save("Asset", [
        decorators.file, decorators.mimeType, decorators.created, decorators.modified,
        decorators.parent, decorators.document, "migration", "custom"], identity);
    ContentType.save("Article", [
        "web", decorators.document, decorators.created, decorators.modified,
        decorators.parent, "migration"], identity);
    ContentType.save("User", ["user", "migration"], identity);

}

function createIndexes(database){
    // parent path should be indexed for quick resolving of parent-child relationships. it is not unique.
    Mongo.createIndex("documents", {'source.current.parent.path' : 1}, {"unique" : false, "background" : true}, database);
    Mongo.createIndex("documents", {'contentType' : 1}, {"background" : true}, database);
    Mongo.createIndex("documents", {'source.current.parent.id' : 1}, {"background" : true}, database);
    Mongo.createIndex("documents", {'source.current.file.path' : 1}, {"background" : true}, database);
    Mongo.createIndex("documents", {'source.current.migration.action' : 1}, {"background" : true}, database);
    Mongo.createIndex("documents", {'source.current.migration.sourceSystem' : 1}, {"background" : true}, database);
    Mongo.createIndex("documents", {'source.current.migration.targetSystem' : 1}, {"background" : true}, database);
} 

Connecting to REST API

Use XURL.get() to request nodes or users from Drupal:
/**
* Performs a get request to the Drupal Server.
* Throws an error if the get request is not successful.
*/
private function get(server, url) {
    var options = {
    };
    var requestUrl = server.apiUrl :: url;
    var result = XURL.get(requestUrl, options);
    
    Assert.equal(
        result.status.code, 
        200, 
        "Call to GET:" :: requestUrl :: " resulted in " :: result.status.code :: ": " :: result.status.phrase
    );
    
    return result;
}

Convert the raw data to a node OBJECT:

/**
* Gets node details based on the data provided by a get request. 
*/
private function getNodeByData (data) {
    var node = {};
    
    //node metadata
    node.id = data.nid;
    node.vid = data.vid;
    node.isNew = data.is_new;
    node.type = data.type;
    node.title = data.title;
    node.language = data.language;
    node.url = data.url;
    
    //publication status
    node.status = data.status;
    node.promote = data.promote;
    node.sticky = data.sticky;
    
    //dates and author
    node.modifiedDate = data.changed;
    node.createdDate = data.created;
    node.author = data.author;
    
    //log and revision
    node.log = data.log;
    node.revision = data.revision;
    
    //web
    node.body = data.body;

    //content type specific fields
    //TODO: double-check that custom fields always have the same fieldname prefix
    foreach (field, value in data) {
        if (String.startsWith(field, "field_")) {
            node[String.substring(field, String.length("field_"), String.length(field))] = value;
        }
    }

    return (node);   
}

Now we have a neat node object that we can use in the transformation robot:

private function transformDocument(node, server) {
    var file = getFile(server, getFileId(node.document.file));
    var result = {
        "file" : transformFile(file),
        "mimeType" : {
            "type" : file.mimeType
        },
        "document" : {
            "title" : node.title,
            "author" : node.author.id
        },
        "modified" : {
            "date" : parseTimestamp(node.modifiedDate)
        },
        "created" : {
            "date" : parseTimestamp(node.createdDate)
        },
        "parent" : {
            "id" : 0,
            "path" : pathToParent(node.url)
        },
        "migration" : {
            "sourceSystem" : "Drupal",
            "timestamp" : Date.now(),
            "action" : settings.actions[0]
        },
        "custom" : {
            "industry" : node.industry,
            "document_type" : node.document_type
        }
    };
    return result;
}

The result of this transformation is saved in UDM and (if it passes the content type validation) it contains all the necessary fields for the migration.