From 05b0d16c4a686230fea1f41e774ade41c84f3cc3 Mon Sep 17 00:00:00 2001
From: Hage Yaapa <hage.yaapa@in.ibm.com>
Date: Thu, 7 Mar 2019 16:50:29 +0530
Subject: [PATCH] feat: Support "type" key in sub-properties in 3.x

---
 lib/model-builder.js  | 35 +++++++++++++++++------------------
 test/datatype.test.js | 18 ++++++++++++++++++
 2 files changed, 35 insertions(+), 18 deletions(-)

diff --git a/lib/model-builder.js b/lib/model-builder.js
index cd831f43..55983370 100644
--- a/lib/model-builder.js
+++ b/lib/model-builder.js
@@ -849,47 +849,46 @@ ModelBuilder.prototype.getSchemaName = function(name) {
 /**
  * Resolve the type string to be a function, for example, 'String' to String.
  * Returns {Function} if the type is resolved
- * @param {String} type The type string, such as 'number', 'Number', 'boolean',
- * or 'String'. This parameter is case insensitive.
+ * @param {String | Object | Array} prop The object whose type is to be resolved
  */
-ModelBuilder.prototype.resolveType = function(type) {
-  if (!type) {
-    return type;
+ModelBuilder.prototype.resolveType = function(prop, isSubProperty) {
+  if (!prop) {
+    return prop;
   }
-  if (Array.isArray(type) && type.length > 0) {
+  if (Array.isArray(prop) && prop.length > 0) {
     // For array types, the first item should be the type string
-    const itemType = this.resolveType(type[0]);
+    const itemType = this.resolveType(prop[0]);
     if (typeof itemType === 'function') {
       return [itemType];
     } else {
       return itemType; // Not resolved, return the type string
     }
   }
-  if (typeof type === 'string') {
-    const schemaType = ModelBuilder.schemaTypes[type.toLowerCase()] || this.models[type];
+  if (typeof prop === 'string') {
+    const schemaType = ModelBuilder.schemaTypes[prop.toLowerCase()] || this.models[prop];
     if (schemaType) {
       return schemaType;
     } else {
       // The type cannot be resolved, let's create a place holder
-      type = this.define(type, {}, {unresolved: true});
-      return type;
+      prop = this.define(prop, {}, {unresolved: true});
+      return prop;
     }
-  } else if (type.constructor.name === 'Object') {
+  } else if (prop.constructor.name === 'Object') {
     // We also support the syntax {type: 'string', ...}
-    if (type.type) {
-      return this.resolveType(type.type);
+    if (!isSubProperty && prop.type) {
+      return this.resolveType(prop.type, true);
     } else {
       return this.define(this.getSchemaName(null),
-        type, {
+        prop, {
           anonymous: true,
           idInjection: false,
           strict: this.settings.strictEmbeddedModels || false,
         });
     }
-  } else if ('function' === typeof type) {
-    return type;
+  } else if ('function' === typeof prop) {
+    return prop;
   }
-  return type;
+  return prop;
 };
 
 /**
diff --git a/test/datatype.test.js b/test/datatype.test.js
index ee069191..aab8166c 100644
--- a/test/datatype.test.js
+++ b/test/datatype.test.js
@@ -28,6 +28,24 @@ describe('datatypes', function() {
     db.automigrate(['Model'], done);
   });
 
+  it('should resolve top-level "type" property correctly', function() {
+    const Account = db.define('Account', {
+      type: String,
+      id: String,
+    });
+    Account.definition.properties.type.type.should.equal(String);
+  });
+
+  it('should resolve "type" sub-property correctly', function() {
+    const Account = db.define('Account', {
+      item: {type: {
+        itemname: {type: String},
+        type: {type: String},
+      }},
+    });
+    Account.definition.properties.item.type.should.not.equal(String);
+  });
+
   it('should return 400 when property of type array is set to string value',
     function(done) {
       const myModel = db.define('myModel', {