Repository: aliyun/fun
Pull request #217 (2019-05-14 09:50:40)

custom domain compatibility
#216 自定义域名兼容性


Patch:
diff --git a/docs/specs/2018-04-03-zh-cn.md b/docs/specs/2018-04-03-zh-cn.md
index 89e7804..b34317a 100644
--- a/docs/specs/2018-04-03-zh-cn.md
+++ b/docs/specs/2018-04-03-zh-cn.md
@@ -143,11 +143,11 @@ abc.com: # domain name
        RouteConfig:
          routes: 
            '/a':
-             serviceName: serviceA
-             functionName: functionA
+             ServiceName: serviceA
+             FunctionName: functionA
            '/b':
-             serviceName: serviceB
-             functionName: functionB
+             ServiceName: serviceB
+             FunctionName: functionB
 ```
 
 #### Aliyun::Serverless::Api
@@ -624,8 +624,8 @@ PathConfig 用于设置自定义域名中路径与函数的映射关系。
 
 ```
   '/a': # path
-        serviceName: serviceA
-        functionName: functionA
+        ServiceName: serviceA
+        FunctionName: functionA
 ```
 
 #### OSS Key 配置对象
diff --git a/docs/specs/2018-04-03.md b/docs/specs/2018-04-03.md
index bd29742..67d649c 100644
--- a/docs/specs/2018-04-03.md
+++ b/docs/specs/2018-04-03.md
@@ -139,11 +139,11 @@ abc.com: # domain name
        RouteConfig:
          routes: 
            '/a':
-             serviceName: serviceA
-             functionName: functionA
+             ServiceName: serviceA
+             FunctionName: functionA
            '/b':
-             serviceName: serviceB
-             functionName: functionB
+             ServiceName: serviceB
+             FunctionName: functionB
 ```
 
 #### Aliyun::Serverless::Api
@@ -598,8 +598,8 @@ Example：
 
 ```
   '/a': # path
-        serviceName: serviceA
-        functionName: functionA
+        ServiceName: serviceA
+        FunctionName: functionA
 ```
 
 #### OSS Key configuration object
diff --git a/lib/commands/deploy.js b/lib/commands/deploy.js
index 149e0d7..15fd138 100644
--- a/lib/commands/deploy.js
+++ b/lib/commands/deploy.js
@@ -13,7 +13,7 @@ async function deploy(stage, tplPath) {
   if (!tplPath) {
     throw new Error(red('Current folder not a fun project\nThe folder must contains template.[yml|yaml] or faas.[yml|yaml] .'));
   } else if (path.basename(tplPath).startsWith('template')) {
-    await require('../deploy/deploy-by-tpl')(tplPath);
+    await require('../deploy/deploy-by-tpl').deploy(tplPath);
   } else if (path.basename(tplPath).startsWith('faas')) {
     await require('../deploy/deploy-by-faas')(stage);
   } else {
diff --git a/lib/deploy/deploy-by-tpl.js b/lib/deploy/deploy-by-tpl.js
index 00c9a34..5e3e974 100644
--- a/lib/deploy/deploy-by-tpl.js
+++ b/lib/deploy/deploy-by-tpl.js
@@ -268,12 +268,15 @@ async function deployLogstore(projectName, logstoreDefinition) {
 async function deployCustomDomain(domainName, domainDefinition) {
   const properties = (domainDefinition.Properties || {});
   const protocol = properties.Protocol;
-  const tplRouteConfig = properties.RouteConfig.Routes;
+  const tplRouteConfig = properties.RouteConfig.Routes || properties.RouteConfig.routes;
   var routes = [];
   var transRoute = Object.entries(tplRouteConfig);
-  for (var i of transRoute) {
-    i[1].path = i[0];
-    routes.push(i[1]);
+  for (var route of transRoute) {
+    let deformedRoute = _.mapKeys(route[1], function(value, key) {
+      return _.lowerFirst(key);
+    });
+    deformedRoute.path = route[0];
+    routes.push(deformedRoute);
   }
   var routeConfig = {};
   routeConfig.routes = routes;
@@ -521,4 +524,4 @@ async function deploy(tplPath) {
   }
 }
 
-module.exports = deploy;
\ No newline at end of file
+module.exports = { deploy, deployCustomDomain };
\ No newline at end of file
diff --git a/lib/validate/schema/custom-domain-resource.js b/lib/validate/schema/custom-domain-resource.js
index 46880a9..060c618 100644
--- a/lib/validate/schema/custom-domain-resource.js
+++ b/lib/validate/schema/custom-domain-resource.js
@@ -21,17 +21,31 @@ const customDomainResourceSchema = {
             'Routes': {
               'type': 'object',
               'patternProperties': {
-                '^/': {
+                '^/.*$': {
                   'anyOf': [
                     { '$ref': '/Resources/CustomDomain/PathConfig' }
                   ]
                 }
-              }
+              },
+              'additionalProperties': false
+            },
+            'routes': {
+              'type': 'object',
+              'patternProperties': {
+                '^/.*$': {
+                  'anyOf': [
+                    { '$ref': '/Resources/CustomDomain/PathConfig' }
+                  ]
+                }
+              },
+              'additionalProperties': false
             }
           },
+          'patternRequired': [ '[Rr]outes' ],
           'additionalProperties': false
         }
       },
+      'required': ['Protocol', 'RouteConfig'],
       'additionalProperties': false
     }
   },
diff --git a/lib/validate/schema/path-config.js b/lib/validate/schema/path-config.js
index 2a386a8..1366b7f 100644
--- a/lib/validate/schema/path-config.js
+++ b/lib/validate/schema/path-config.js
@@ -4,14 +4,20 @@ const pathConfigResourceSchema = {
   '$id': '/Resources/CustomDomain/PathConfig',
   'type': 'object',
   'properties': {
+    'serviceName': {
+      'type': 'string'
+    },
     'ServiceName': {
       'type': 'string'
     },
+    'functionName': {
+      'type': 'string'
+    },
     'FunctionName': {
       'type': 'string'
     }
   },
-  'required': ['ServiceName', 'FunctionName'],
+  'patternRequired': ['[Ss]erviceName', '[Ff]unctionName'],
   'additionalProperties': false
 };
 


Test patch:
diff --git a/test/deploy/deploy-by-tpl.test.js b/test/deploy/deploy-by-tpl.test.js
index 42cd453..4d6fba2 100644
--- a/test/deploy/deploy-by-tpl.test.js
+++ b/test/deploy/deploy-by-tpl.test.js
@@ -52,7 +52,7 @@ describe('deploy service role ', () => {
     await proxyquire('../../lib/deploy/deploy-by-tpl', {
       './deploy-support': deploySupport,
       '../ram': ram
-    })(path.join('./examples', example, 'template.yml'));
+    }).deploy(path.join('./examples', example, 'template.yml'));
   }
 
   it('all none', async ()=>{
@@ -129,7 +129,7 @@ describe('deploy', () => {
     await proxyquire('../../lib/deploy/deploy-by-tpl', {
       './deploy-support': deploySupport,
       '../ram': ram
-    })(path.join('./examples', example, 'template.yml'));
+    }).deploy(path.join('./examples', example, 'template.yml'));
   }
 
   it('deploy datahub', async () => {
@@ -830,13 +830,13 @@ describe('deploy', () => {
       routeConfig: {
         routes: [{
           path: '/a',
-          ServiceName: 'serviceA',
-          FunctionName: 'functionA'
+          serviceName: 'serviceA',
+          functionName: 'functionA'
         },
         {
           path: '/b',
-          ServiceName: 'serviceB',
-          FunctionName: 'functionB'
+          serviceName: 'serviceB',
+          functionName: 'functionB'
         }
         ]
       }
@@ -930,3 +930,102 @@ describe('deploy', () => {
   });
 });
 
+describe('custom domain', () => {
+  let restoreProcess;
+
+  beforeEach(() => {
+    Object.keys(deploySupport).forEach(m => {
+      sandbox.stub(deploySupport, m);
+    });
+    restoreProcess = setProcess({
+      ACCOUNT_ID: 'ACCOUNT_ID',
+      DEFAULT_REGION: 'cn-shanghai',
+      ACCESS_KEY_ID: 'ACCESS_KEY_ID',
+      ACCESS_KEY_SECRET: 'ACCESS_KEY_SECRET'
+    });
+  });
+  afterEach(() => {
+    sandbox.restore();
+    restoreProcess();
+  });
+
+  async function customDomain(domainName, domainDefinition) {
+    await proxyquire('../../lib/deploy/deploy-by-tpl', {
+      './deploy-support': deploySupport
+    }).deployCustomDomain(domainName, domainDefinition);
+  }
+
+  it('lowercase custom domain', async () =>{
+    await customDomain('domainName', {
+      'Type': 'Aliyun::Serverless::CustomDomain',
+      'Properties': {
+        'Protocol': 'HTTP',
+        'RouteConfig': {
+          'Routes': {
+            '/a': {
+              'serviceName': 'serviceA',
+              'functionName': 'functionA'
+            },
+            '/b': {
+              'serviceName': 'serviceB',
+              'functionName': 'functionB'
+            }
+          }
+        }
+      }
+    });
+    assert.calledWith(deploySupport.makeCustomDomain, {
+      domainName: 'domainName',
+      protocol: 'HTTP',
+      routeConfig: {
+        routes: [{
+          path: '/a',
+          serviceName: 'serviceA',
+          functionName: 'functionA'
+        },
+        {
+          path: '/b',
+          serviceName: 'serviceB',
+          functionName: 'functionB'
+        }]
+      }
+    });
+  });
+  it('capital custom domain', async () =>{
+    await customDomain('domainName', {
+      'Type': 'Aliyun::Serverless::CustomDomain',
+      'Properties': {
+        'Protocol': 'HTTP',
+        'RouteConfig': {
+          'Routes': {
+            '/a': {
+              'ServiceName': 'serviceA',
+              'FunctionName': 'functionA'
+            },
+            '/b': {
+              'ServiceName': 'serviceB',
+              'FunctionName': 'functionB'
+            }
+          }
+        }
+      }
+    });
+    assert.calledWith(deploySupport.makeCustomDomain, {
+      domainName: 'domainName',
+      protocol: 'HTTP',
+      routeConfig: {
+        routes: [{
+          path: '/a',
+          serviceName: 'serviceA',
+          functionName: 'functionA'
+        },
+        {
+          path: '/b',
+          serviceName: 'serviceB',
+          functionName: 'functionB'
+        }]
+      }
+    });
+  });
+});
+