Merge branch 'develop' into service-test
diff --git a/dubbo-admin-backend/pom.xml b/dubbo-admin-backend/pom.xml
index 7986fbd..d2510ae 100644
--- a/dubbo-admin-backend/pom.xml
+++ b/dubbo-admin-backend/pom.xml
@@ -92,6 +92,10 @@
</exclusions>
</dependency>
<dependency>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-recipes</artifactId>
+ </dependency>
+ <dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
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
new file mode 100644
index 0000000..3ded7c2
--- /dev/null
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/controller/ServiceTestController.java
@@ -0,0 +1,20 @@
+package org.apache.dubbo.admin.controller;
+
+import org.apache.dubbo.admin.model.dto.ServiceTestDTO;
+import org.apache.dubbo.admin.service.impl.GenericServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/api/{env}/test")
+public class ServiceTestController {
+
+ @Autowired
+ private GenericServiceImpl genericService;
+
+ @RequestMapping(method = RequestMethod.POST)
+ public Object test(@PathVariable String env, @RequestBody ServiceTestDTO serviceTestDTO) {
+ return genericService.invoke(serviceTestDTO.getService(), serviceTestDTO.getMethod(), serviceTestDTO.getTypes(), null);
+// return null;
+ }
+}
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/model/dto/ServiceTestDTO.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/model/dto/ServiceTestDTO.java
new file mode 100644
index 0000000..7934e7c
--- /dev/null
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/model/dto/ServiceTestDTO.java
@@ -0,0 +1,40 @@
+package org.apache.dubbo.admin.model.dto;
+
+public class ServiceTestDTO {
+ private String service;
+ private String method;
+ private String[] types;
+ private String params;
+
+ public String getService() {
+ return service;
+ }
+
+ public void setService(String service) {
+ this.service = service;
+ }
+
+ public String getMethod() {
+ return method;
+ }
+
+ public void setMethod(String method) {
+ this.method = method;
+ }
+
+ public String[] getTypes() {
+ return types;
+ }
+
+ public void setTypes(String[] types) {
+ this.types = types;
+ }
+
+ public String getParams() {
+ return params;
+ }
+
+ public void setParams(String params) {
+ this.params = params;
+ }
+}
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/GenericServiceImpl.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/GenericServiceImpl.java
new file mode 100644
index 0000000..6b6e748
--- /dev/null
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/GenericServiceImpl.java
@@ -0,0 +1,42 @@
+package org.apache.dubbo.admin.service.impl;
+
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.config.ReferenceConfig;
+import org.apache.dubbo.config.RegistryConfig;
+import org.apache.dubbo.registry.Registry;
+import org.apache.dubbo.rpc.service.GenericService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+
+@Component
+public class GenericServiceImpl {
+
+ private ReferenceConfig<GenericService> reference;
+
+ @Autowired
+ private Registry registry;
+
+ @PostConstruct
+ public void init() {
+ reference = new ReferenceConfig<>();
+ reference.setGeneric(true);
+
+ RegistryConfig registryConfig = new RegistryConfig();
+ registryConfig.setAddress(registry.getUrl().getProtocol() + "://" + registry.getUrl().getAddress());
+
+ ApplicationConfig applicationConfig = new ApplicationConfig();
+ applicationConfig.setName("dubbo-admin");
+ applicationConfig.setRegistry(registryConfig);
+
+ reference.setApplication(applicationConfig);
+ }
+
+ public Object invoke(String service, String method, String[] parameterTypes, Object[] params) {
+
+ reference.setInterface(service);
+ GenericService genericService = reference.get();
+ return genericService.$invoke(method, parameterTypes, params);
+ }
+}
diff --git a/dubbo-admin-frontend/package.json b/dubbo-admin-frontend/package.json
index e4a3919..26e89c6 100644
--- a/dubbo-admin-frontend/package.json
+++ b/dubbo-admin-frontend/package.json
@@ -15,6 +15,7 @@
"brace": "^0.11.1",
"http-status": "^1.2.0",
"js-yaml": "^3.12.0",
+ "jsoneditor": "^5.26.2",
"vue": "^2.5.2",
"vue-router": "^3.0.1",
"vuetify": "^1.2.2",
diff --git a/dubbo-admin-frontend/src/components/ServiceTest.vue b/dubbo-admin-frontend/src/components/ServiceTest.vue
new file mode 100644
index 0000000..b1f05b2
--- /dev/null
+++ b/dubbo-admin-frontend/src/components/ServiceTest.vue
@@ -0,0 +1,170 @@
+<!--
+ - Licensed to the Apache Software Foundation (ASF) under one or more
+ - contributor license agreements. See the NOTICE file distributed with
+ - this work for additional information regarding copyright ownership.
+ - The ASF licenses this file to You under the Apache License, Version 2.0
+ - (the "License"); you may not use this file except in compliance with
+ - the License. You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+ -->
+<template>
+ <v-container grid-list-xl fluid>
+ <v-layout row wrap>
+ <v-flex xs12>
+ <search v-model="filter" label="Search by service name" :submit="search"></search>
+ </v-flex>
+ <v-flex xs12>
+ <h3>Methods</h3>
+ </v-flex>
+ <v-flex xs12>
+ <v-data-table :headers="headers" :items="methods" hide-actions class="elevation-1">
+ <template slot="items" slot-scope="props">
+ <td>{{ props.item.name }}</td>
+ <td><v-chip xs v-for="(type, index) in props.item.parameterTypes" :key="index" label>{{ type }}</v-chip></td>
+ <td><v-chip label>{{ props.item.returnType }}</v-chip></td>
+ <td class="text-xs-right">
+ <v-tooltip bottom>
+ <v-icon small
+ class="mr-2"
+ color="blue"
+ slot="activator"
+ @click="toTest(props.item)">input</v-icon>
+ <span>Try it</span>
+ </v-tooltip>
+ </td>
+ </template>
+ </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>
+ </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>
+
+<script>
+ import JsonEditor from '@/components/public/JsonEditor'
+ import Search from '@/components/public/Search'
+
+ export default {
+ name: 'ServiceTest',
+ data () {
+ return {
+ filter: 'org.apache.dubbo.demo.api.DemoService',
+ headers: [
+ {
+ text: 'Method Name',
+ value: 'method',
+ sortable: false
+ },
+ {
+ text: 'Parameter List',
+ value: 'parameter',
+ sortable: false
+ },
+ {
+ text: 'Return Type',
+ value: 'returnType',
+ sortable: false
+ },
+ {
+ text: '',
+ value: 'operation',
+ sortable: false
+ }
+ ],
+ service: null,
+ methods: [],
+ modal: {
+ method: null,
+ enable: false,
+ types: null,
+ json: []
+ }
+ }
+ },
+ methods: {
+ search () {
+ if (this.filter == null) {
+ this.filter = ''
+ }
+ this.$router.push({
+ path: 'test',
+ query: { service: this.filter }
+ })
+ this.$axios.get('/service/' + this.filter).then(response => {
+ this.service = response.data
+ if (this.service.hasOwnProperty('metadata')) {
+ this.methods = this.service.metadata.methods
+ }
+ }).catch(error => {
+ this.showSnackbar('error', error.response.data.message)
+ })
+ },
+ toTest (item) {
+ Object.assign(this.modal, {
+ enable: true,
+ method: item.name
+ })
+ this.modal.json = []
+ this.modal.types = item.parameterTypes
+ item.parameterTypes.forEach((i, index) => {
+ this.modal.json.push(this.getType(i))
+ })
+ },
+ test () {
+ this.$axios.post('/test', {
+ service: this.service.metadata.canonicalName,
+ method: this.modal.method,
+ types: this.modal.types,
+ params: JSON.stringify(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 ''
+ }
+ }
+ },
+ components: {
+ JsonEditor,
+ Search
+ }
+ }
+</script>
diff --git a/dubbo-admin-frontend/src/components/public/Footers.vue b/dubbo-admin-frontend/src/components/public/Footers.vue
index a5bbab6..331bea7 100644
--- a/dubbo-admin-frontend/src/components/public/Footers.vue
+++ b/dubbo-admin-frontend/src/components/public/Footers.vue
@@ -16,7 +16,7 @@
-->
<template>
- <v-footer inset height="auto" class="pa-3 footer-border-top" app>
+ <v-footer inset height="auto" class="pa-3 footer-border-top">
<v-spacer></v-spacer>
<span class="caption mr-1"><strong>Copyright</strong> ©{{ new Date().getFullYear() }} <strong>The Apache Software Foundation.</strong></span>
</v-footer>
diff --git a/dubbo-admin-frontend/src/components/public/JsonEditor.vue b/dubbo-admin-frontend/src/components/public/JsonEditor.vue
new file mode 100644
index 0000000..c4cbbf5
--- /dev/null
+++ b/dubbo-admin-frontend/src/components/public/JsonEditor.vue
@@ -0,0 +1,84 @@
+<!--
+ - Licensed to the Apache Software Foundation (ASF) under one or more
+ - contributor license agreements. See the NOTICE file distributed with
+ - this work for additional information regarding copyright ownership.
+ - The ASF licenses this file to You under the Apache License, Version 2.0
+ - (the "License"); you may not use this file except in compliance with
+ - the License. You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+ -->
+<template>
+ <div class="jsoneditor-vue-container"></div>
+</template>
+
+<script>
+ import JSONEditor from 'jsoneditor'
+ import 'jsoneditor/dist/jsoneditor.css'
+
+ export default {
+ name: 'json-editor',
+ props: {
+ value: Object,
+ mode: {
+ type: String,
+ default: 'tree'
+ },
+ modes: {
+ type: Array,
+ default: () => ['tree', 'code']
+ },
+ templates: Array
+ },
+ data () {
+ return {
+ $jsoneditor: null
+ }
+ },
+ watch: {
+ value (newVal, oldVal) {
+ if (newVal !== oldVal && this.$jsoneditor) {
+ this.$jsoneditor.update(newVal)
+ }
+ }
+ },
+ mounted () {
+ const options = {
+ name: 'Parameters',
+ navigationBar: false,
+ search: false,
+ mode: this.mode,
+ modes: this.modes,
+ onChange: () => {
+ if (this.$jsoneditor) {
+ var 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.expandAll()
+ },
+ beforeDestroy () {
+ if (this.$jsoneditor) {
+ this.$jsoneditor.destroy()
+ this.$jsoneditor = null
+ }
+ }
+ }
+</script>
+
+<style scoped>
+ .jsoneditor-vue-container {
+ width: 100%;
+ height: 100%;
+ }
+</style>
diff --git a/dubbo-admin-frontend/src/router/index.js b/dubbo-admin-frontend/src/router/index.js
index 38dc6f0..2fe356e 100644
--- a/dubbo-admin-frontend/src/router/index.js
+++ b/dubbo-admin-frontend/src/router/index.js
@@ -19,6 +19,7 @@
import Router from 'vue-router'
import ServiceSearch from '@/components/ServiceSearch'
import ServiceDetail from '@/components/ServiceDetail'
+import ServiceTest from '@/components/ServiceTest'
import RoutingRule from '@/components/governance/RoutingRule'
import TagRule from '@/components/governance/TagRule'
import AccessControl from '@/components/governance/AccessControl'
@@ -41,6 +42,11 @@
component: ServiceDetail
},
{
+ path: '/test',
+ name: 'ServiceTest',
+ component: ServiceTest
+ },
+ {
path: '/governance/routingRule',
name: 'RoutingRule',
component: RoutingRule
diff --git a/pom.xml b/pom.xml
index 44eb93a..abee9bd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -109,6 +109,17 @@
<version>${curator-version}</version>
</dependency>
<dependency>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-recipes</artifactId>
+ <version>${curator-version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.zookeeper</groupId>
+ <artifactId>zookeeper</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson-version}</version>