test service's parameterized methods (#277)

diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/util/ServiceTestUtil.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/util/ServiceTestUtil.java
index ad84fd0..c65a244 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/util/ServiceTestUtil.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/util/ServiceTestUtil.java
@@ -49,10 +49,10 @@
         MethodMetadata methodMetadata = new MethodMetadata();
         String[] parameterTypes = methodDefinition.getParameterTypes();
         String returnType = methodDefinition.getReturnType();
-        String signature = methodDefinition.getName() + "~" + Arrays.stream(parameterTypes).collect(Collectors.joining(";"));
+        String signature = methodDefinition.getName() + "~" + String.join(";", parameterTypes);
         methodMetadata.setSignature(signature);
         methodMetadata.setReturnType(returnType);
-        List parameters = generateParameterTypes(parameterTypes, serviceDefinition);
+        List<Object> parameters = generateParameterTypes(parameterTypes, serviceDefinition);
         methodMetadata.setParameterTypes(parameters);
         return methodMetadata;
     }
@@ -72,8 +72,8 @@
                 type.equals("java.lang.Object");
     }
 
-    private static List generateParameterTypes(String[] parameterTypes, ServiceDefinition serviceDefinition) {
-        List parameters = new ArrayList();
+    private static List<Object> generateParameterTypes(String[] parameterTypes, ServiceDefinition serviceDefinition) {
+        List<Object> parameters = new ArrayList<>();
         for (String type : parameterTypes) {
             Object result = generateType(serviceDefinition, type);
             parameters.add(result);
@@ -87,7 +87,7 @@
                 .findFirst().orElse(new TypeDefinition(type));
     }
 
-    private static void generateComplexType(ServiceDefinition sd, TypeDefinition td, Map holder) {
+    private static void generateComplexType(ServiceDefinition sd, TypeDefinition td, Map<String, Object> holder) {
         for (Map.Entry<String, TypeDefinition> entry : td.getProperties().entrySet()) {
             if (isPrimitiveType(td)) {
                 holder.put(entry.getKey(), generatePrimitiveType(td));
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/controller/ServiceTestController.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/controller/ServiceTestController.java
index a39c063..8279212 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/controller/ServiceTestController.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/controller/ServiceTestController.java
@@ -20,12 +20,13 @@
 @RestController
 @RequestMapping("/api/{env}/test")
 public class ServiceTestController {
+    private final GenericServiceImpl genericService;
+    private final ProviderService providerService;
 
-    @Autowired
-    private GenericServiceImpl genericService;
-
-    @Autowired
-    private ProviderService providerService;
+    public ServiceTestController(GenericServiceImpl genericService, ProviderService providerService) {
+        this.genericService = genericService;
+        this.providerService = providerService;
+    }
 
     @RequestMapping(method = RequestMethod.POST)
     public Object test(@PathVariable String env, @RequestBody ServiceTestDTO serviceTestDTO) {
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/model/domain/MethodMetadata.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/model/domain/MethodMetadata.java
index 25a98c9..1889e7b 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/model/domain/MethodMetadata.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/model/domain/MethodMetadata.java
@@ -22,7 +22,7 @@
 
 public class MethodMetadata {
     private String signature;
-    private List parameterTypes;
+    private List<Object> parameterTypes;
     private String returnType;
 
     public String getSignature() {
@@ -33,11 +33,11 @@
         this.signature = signature;
     }
 
-    public List getParameterTypes() {
+    public List<Object> getParameterTypes() {
         return parameterTypes;
     }
 
-    public void setParameterTypes(List parameterTypes) {
+    public void setParameterTypes(List<Object> parameterTypes) {
         this.parameterTypes = parameterTypes;
     }
 
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/metadata/impl/ZookeeperMetaDataCollector.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/metadata/impl/ZookeeperMetaDataCollector.java
index 48c3cf3..af334e3 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/metadata/impl/ZookeeperMetaDataCollector.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/metadata/impl/ZookeeperMetaDataCollector.java
@@ -27,6 +27,8 @@
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.metadata.identifier.MetadataIdentifier;
 
+import javax.annotation.PostConstruct;
+
 public class ZookeeperMetaDataCollector implements MetaDataCollector {
 
     private static final Logger logger = LoggerFactory.getLogger(ZookeeperMetaDataCollector.class);
diff --git a/dubbo-admin-backend/src/main/resources/application.properties b/dubbo-admin-backend/src/main/resources/application.properties
index 514345f..e765b06 100644
--- a/dubbo-admin-backend/src/main/resources/application.properties
+++ b/dubbo-admin-backend/src/main/resources/application.properties
@@ -19,6 +19,7 @@
 admin.registry.address=zookeeper://127.0.0.1:2181
 admin.config-center=zookeeper://127.0.0.1:2181
 admin.metadata.address=zookeeper://127.0.0.1:2181
+dubbo.configcenter="zookeeper://127.0.0.1:2181"
 
 
 
diff --git a/dubbo-admin-frontend/src/components/public/JsonEditor.vue b/dubbo-admin-frontend/src/components/public/JsonEditor.vue
index 327e5ff..f420cf8 100644
--- a/dubbo-admin-frontend/src/components/public/JsonEditor.vue
+++ b/dubbo-admin-frontend/src/components/public/JsonEditor.vue
@@ -34,7 +34,15 @@
         type: Array,
         default: () => ['tree', 'code']
       },
-      templates: Array
+      templates: Array,
+      name: {
+        type: String,
+        default: 'Parameters'
+      },
+      readonly: {
+        type: Boolean,
+        default: false
+      }
     },
     data () {
       return {
@@ -44,27 +52,28 @@
     watch: {
       value (newVal, oldVal) {
         if (newVal !== oldVal && this.$jsoneditor) {
-          this.$jsoneditor.update(newVal)
+          this.$jsoneditor.update(newVal || {})
         }
       }
     },
     mounted () {
       const options = {
-        name: 'Parameters',
+        name: this.name,
         navigationBar: false,
         search: false,
         mode: this.mode,
         modes: this.modes,
+        onEditable: (node) => !this.readonly,
         onChange: () => {
           if (this.$jsoneditor) {
-            var json = this.$jsoneditor.get()
+            const json = this.$jsoneditor.get()
             this.$emit('input', json)
           }
         },
         templates: this.templates
       }
       this.$jsoneditor = new JSONEditor(this.$el, options)
-      this.$jsoneditor.set(this.value)
+      this.$jsoneditor.set(this.value || {})
       this.$jsoneditor.expandAll()
     },
     beforeDestroy () {
diff --git a/dubbo-admin-frontend/src/components/public/Search.vue b/dubbo-admin-frontend/src/components/public/Search.vue
index 15e3432..92d5ef2 100644
--- a/dubbo-admin-frontend/src/components/public/Search.vue
+++ b/dubbo-admin-frontend/src/components/public/Search.vue
@@ -23,7 +23,9 @@
           :label="label" clearable
           :hint="hint"
           v-bind:value="value"
-          v-on:input="$emit('input', $event)"></v-text-field>
+          v-on:input="$emit('input', $event)"
+          @keydown.enter="submit"
+        ></v-text-field>
         <v-btn @click="submit" color="primary" large>{{$t('search')}}</v-btn>
       </v-layout>
     </v-card-text>
diff --git a/dubbo-admin-frontend/src/components/test/ServiceTest.vue b/dubbo-admin-frontend/src/components/test/ServiceTest.vue
index 2d08265..35b9eec 100644
--- a/dubbo-admin-frontend/src/components/test/ServiceTest.vue
+++ b/dubbo-admin-frontend/src/components/test/ServiceTest.vue
@@ -18,7 +18,7 @@
   <v-container grid-list-xl fluid>
     <v-layout row wrap>
       <v-flex xs12>
-        <search id="serviceSearch" v-model="filter" label="Search by service name" :submit="search"></search>
+        <search v-model="filter" label="Search by service name" :submit="search"></search>
       </v-flex>
       <v-flex xs12>
         <h3>Methods</h3>
@@ -44,33 +44,6 @@
         </v-data-table>
       </v-flex>
     </v-layout>
-
-    <v-dialog v-model="modal.enable" width="1000px" persistent>
-      <v-card>
-        <v-card-title>
-          <span class="headline">Test {{ modal.method }}</span>
-        </v-card-title>
-        <v-container grid-list-xl fluid>
-          <v-layout row>
-            <v-flex lg6>
-              <json-editor v-model="modal.json" />
-            </v-flex>
-            <v-flex lg6>
-              <json-editor v-model="modal.json" />
-            </v-flex>
-          </v-layout>
-        </v-container>
-        <v-card-actions>
-          <v-spacer></v-spacer>
-          <v-btn color="darken-1"
-                 flat
-                 @click="modal.enable = false">Close</v-btn>
-          <v-btn color="primary"
-                 depressed
-                 @click="test">Execute</v-btn>
-        </v-card-actions>
-      </v-card>
-    </v-dialog>
   </v-container>
 </template>
 
@@ -80,9 +53,13 @@
 
   export default {
     name: 'ServiceTest',
+    components: {
+      JsonEditor,
+      Search
+    },
     data () {
       return {
-        filter: '',
+        filter: this.$route.query['service'] || '',
         headers: [
           {
             text: 'Method Name',
@@ -106,35 +83,26 @@
           }
         ],
         service: null,
-        methods: [],
-        modal: {
-          method: null,
-          enable: false,
-          parameterTypes: null,
-          json: []
-        }
+        methods: []
       }
     },
     methods: {
       search () {
         if (!this.filter) {
-          this.filter = document.querySelector('#serviceSearch').value.trim()
-          if (!this.filter) {
-            return
-          }
+          return
         }
-        this.$router.push({
-          path: 'test',
+        this.$router.replace({
           query: { service: this.filter }
         })
         this.$axios.get('/service/' + this.filter).then(response => {
           this.service = response.data
-          if (this.service.hasOwnProperty('metadata')) {
-            let data = this.service.metadata.methods
-            for (let i = 0; i < data.length; i++) {
+          this.methods = []
+          if (this.service.metadata) {
+            let methods = this.service.metadata.methods
+            for (let i = 0; i < methods.length; i++) {
               let method = {}
-              let sig = data[i].name + '~'
-              let parameters = data[i].parameterTypes
+              let sig = methods[i].name + '~'
+              let parameters = methods[i].parameterTypes
               let length = parameters.length
               for (let j = 0; j < length; j++) {
                 sig = sig + parameters[j]
@@ -143,9 +111,9 @@
                 }
               }
               method.signature = sig
-              method.name = data[i].name
-              method.parameterTypes = data[i].parameterTypes
-              method.returnType = data[i].returnType
+              method.name = methods[i].name
+              method.parameterTypes = methods[i].parameterTypes
+              method.returnType = methods[i].returnType
               method.service = response.data.service
               method.application = response.data.application
               this.methods.push(method)
@@ -155,58 +123,12 @@
           this.showSnackbar('error', error.response.data.message)
         })
       },
-      toTest (item) {
-        Object.assign(this.modal, {
-          enable: true,
-          method: item.name
-        })
-        this.modal.json = []
-        this.modal.parameterTypes = item.parameterTypes
-        item.parameterTypes.forEach((i, index) => {
-          this.modal.json.push(this.getType(i))
-        })
-      },
       getHref (application, service, method) {
-        let base = '/#/testMethod?'
-        let query = 'application=' + application + '&service=' + service + '&method=' + method
-        return base + query
-      },
-      test () {
-        this.$axios.post('/test', {
-          service: this.service.metadata.canonicalName,
-          method: this.modal.method,
-          parameterTypes: this.modal.parameterTypes,
-          params: this.modal.json
-        }).then(response => {
-          console.log(response)
-        })
-      },
-      getType (type) {
-        if (type.indexOf('java.util.List') === 0) {
-          return []
-        } else if (type.indexOf('java.util.Map') === 0) {
-          return []
-        } else {
-          return ''
-        }
+        return `/#/testMethod?application=${application}&service=${service}&method=${method}`
       }
     },
-    mounted: function () {
-      let query = this.$route.query
-      let filter = null
-      Object.keys(query).forEach(function (key) {
-        if (key === 'service') {
-          filter = query[key]
-        }
-      })
-      if (filter !== null) {
-        this.filter = filter
-        this.search()
-      }
-    },
-    components: {
-      JsonEditor,
-      Search
+    created () {
+      this.search()
     }
   }
 </script>
diff --git a/dubbo-admin-frontend/src/components/test/TestMethod.vue b/dubbo-admin-frontend/src/components/test/TestMethod.vue
index 9657f92..a5093f1 100644
--- a/dubbo-admin-frontend/src/components/test/TestMethod.vue
+++ b/dubbo-admin-frontend/src/components/test/TestMethod.vue
@@ -18,110 +18,85 @@
 <template>
   <v-container grid-list-xl fluid>
     <v-layout row wrap>
-      <v-flex lg12>
-      </v-flex>
-      <v-flex lg12>
+      <v-flex class="test-form" lg12 xl6>
         <v-card>
-          <v-flex lg8>
-            <v-card-title>
-              <span>Service: {{service}}<br> Method: {{method.name}}</span>
-            </v-card-title>
-            <v-card-text>
-              <json-editor id="test" v-model="method.json"/>
-            </v-card-text>
-            <v-card-actions>
-              <v-spacer></v-spacer>
-              <v-btn id="execute" mt-0 color="primary" @click="executeMethod()">EXECUTE</v-btn>
-            </v-card-actions>
-          </v-flex>
-          <v-flex lg8>
-            <v-card-text>
-              <h2>Test Result</h2>
-              <json-editor id="result" v-model="result" v-if="showResult"></json-editor>
-            </v-card-text>
-          </v-flex>
+          <v-card-title class="headline">Test: {{service}}#{{method.name}}</v-card-title>
+          <v-card-text>
+            <json-editor id="test" v-model="method.json"/>
+          </v-card-text>
+          <v-card-actions>
+            <v-spacer></v-spacer>
+            <v-btn id="execute" mt-0 color="primary" @click="executeMethod()">EXECUTE</v-btn>
+          </v-card-actions>
         </v-card>
       </v-flex>
-
+      <v-flex class="test-result" lg12 xl6>
+        <v-card>
+          <v-card-title class="headline">Result</v-card-title>
+          <v-card-text>
+            <json-editor v-model="result" name="Result" readonly></json-editor>
+          </v-card-text>
+        </v-card>
+      </v-flex>
     </v-layout>
-
   </v-container>
 </template>
 
 <script>
   import JsonEditor from '@/components/public/JsonEditor'
+
   export default {
     name: 'TestMethod',
+    components: {
+      JsonEditor
+    },
     data () {
       return {
-        basic: [],
-        service: null,
-        application: null,
+        service: this.$route.query['service'],
+        application: this.$route.query['application'],
         method: {
           name: null,
           parameterTypes: [],
           json: []
         },
-        showResult: false,
-        result: {}
+        result: null
       }
     },
-
     methods: {
-      executeMethod: function () {
-        let serviceTestDTO = {}
-        serviceTestDTO.service = this.service
-        serviceTestDTO.method = this.method.name
-        serviceTestDTO.parameterType = this.method.parameterTypes
-        serviceTestDTO.params = this.method.json
-        this.$axios.post('/test', serviceTestDTO)
-          .then(response => {
-            if (response.status === 200) {
-              this.result = response.data
-              this.showResult = true
-            }
-          })
+      executeMethod () {
+        let serviceTestDTO = {
+          service: this.service,
+          method: this.method.name,
+          parameterTypes: this.method.parameterTypes,
+          params: this.method.json
+        }
+        this.$axios.post('/test', serviceTestDTO).then(response => {
+          if (response.status === 200) {
+            this.result = response.data
+          }
+        })
       }
     },
+    mounted () {
+      const query = this.$route.query
+      const method = query['method']
 
-    mounted: function () {
-      let query = this.$route.query
-      let vm = this
-      let method = null
-      Object.keys(query).forEach(function (key) {
-        if (key === 'service') {
-          let item = {}
-          item.name = 'service'
-          item.value = query[key]
-          vm.basic.push(item)
-          vm.service = query[key]
-        }
-        if (key === 'method') {
-          let item = {}
-          item.name = 'method'
-          item.value = query[key]
-          vm.method.name = query[key].split('~')[0]
-          method = query[key]
-          vm.basic.push(item)
-        }
-        if (key === 'application') {
-          vm.application = query[key]
-        }
-      })
+      if (method) {
+        const [methodName, parametersTypes] = method.split('~')
+        this.method.name = methodName
+        this.method.parameterTypes = parametersTypes.split(';')
+      }
+
       this.$axios.get('/test/method', {
         params: {
-          application: vm.application,
-          service: vm.service,
+          application: this.application,
+          service: this.service,
           method: method
         }
       }).then(response => {
         this.method.json = response.data.parameterTypes
       })
-    },
-    components: {
-      JsonEditor
     }
-
   }
 </script>