This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-cli.git


The following commit(s) were added to refs/heads/master by this push:
     new 6542344  [Feature] Support multiple linear metrics and enhance Ascii 
Graph Display (#30)
6542344 is described below

commit 6542344a89605f04fa32471da4bd5575df677bee
Author: kezhenxu94 <[email protected]>
AuthorDate: Fri Mar 6 20:48:25 2020 +0800

    [Feature] Support multiple linear metrics and enhance Ascii Graph Display 
(#30)
    
    ### Motivation
    
    Adopt the new API of multiple linear metrics, 
https://github.com/apache/skywalking/pull/4214
    
    ### Modification
    
    - Rewrite the pxx metrics with the new API
    
    - Slightly refactor the GraphQL client into individual package, according 
to the query protocol
    
    - Enhance the Ascii Graph display style
    
    ### Result
    
    - CLI is compatible with the latest backend
    
    - Ascii Graph can display multiple charts in one screen
---
 .golangci.yml                                      |   2 +-
 Makefile                                           |   5 +-
 README.md                                          | 101 ++++++++++++--
 commands/endpoint/list.go                          |   5 +-
 commands/instance/instance.go                      |   5 +-
 commands/instance/list.go                          |   5 +-
 commands/instance/search.go                        |   5 +-
 commands/interceptor/duration.go                   |  12 +-
 commands/metrics/linear/linear-metrics.go          |  22 +--
 ...inear-metrics.go => multiple-linear-metrics.go} |  39 ++++--
 .../graph/graph.go => commands/metrics/metrics.go  |  24 ++--
 commands/metrics/single/single-metrics.go          |   9 +-
 commands/service/list.go                           |   7 +-
 display/display.go                                 |   8 +-
 display/graph/graph.go                             |  14 +-
 display/graph/linear/linear.go                     |  82 +++++++----
 graphql/client/client.go                           | 150 +--------------------
 graphql/metadata/metadata.go                       |  93 ++++++++++++-
 graphql/metrics/metrics.go                         |  82 +++++++++++
 graphql/{metadata/metadata.go => utils/adapter.go} |  37 ++---
 graphql/{schema => utils}/constants.go             |  32 +++--
 swctl/main.go                                      |  12 +-
 22 files changed, 468 insertions(+), 283 deletions(-)

diff --git a/.golangci.yml b/.golangci.yml
index cebf66b..26b94e2 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -38,7 +38,7 @@ linters-settings:
   misspell:
     locale: US
   lll:
-    line-length: 120
+    line-length: 150
   goimports:
     local-prefixes: github.com/apache/skywalking-cli
   gocritic:
diff --git a/Makefile b/Makefile
index 8ddb4eb..885df84 100644
--- a/Makefile
+++ b/Makefile
@@ -57,7 +57,7 @@ $(PLATFORMS):
        GOOS=$(os) GOARCH=$(ARCH) $(GO_BUILD) $(GO_BUILD_FLAGS) -ldflags 
"$(GO_BUILD_LDFLAGS)" -o $(OUT_DIR)/$(BINARY)-$(VERSION)-$(os)-$(ARCH) 
swctl/main.go
 
 .PHONY: lint
-lint:
+lint: codegen
        $(GO_LINT) version || curl -sfL 
https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh 
-s -- -b $(GO_PATH)/bin v1.21.0
        $(GO_LINT) run -v ./...
 
@@ -86,7 +86,7 @@ coverage: test
        bash <(curl -s https://codecov.io/bash) -t 
a5af28a3-92a2-4b35-9a77-54ad99b1ae00
 
 .PHONY: clean
-clean:
+clean: codegen
        $(GO_CLEAN) ./...
        -rm -rf bin
        -rm -rf coverage.txt
@@ -94,6 +94,7 @@ clean:
        -rm -rf *.tgz
        -rm -rf *.asc
        -rm -rf *.sha512
+       -rm -rf query-protocol/schema.graphqls
 
 release-src: clean
        -tar -zcvf $(RELEASE_SRC).tgz \
diff --git a/README.md b/README.md
index 00f225c..0467d09 100644
--- a/README.md
+++ b/README.md
@@ -149,11 +149,13 @@ This section covers all the available commands in 
SkyWalking CLI and their usage
 
 </details>
 
-### `linear-metrics`
+### `metrics`
+
+#### `metrics linear`
 
 <details>
 
-<summary>linear-metrics [--start=start-time] [--end=end-time] 
--name=metrics-name [--id=entity-id]</summary>
+<summary>metrics linear [--start=start-time] [--end=end-time] 
--name=metrics-name [--id=entity-id]</summary>
 
 | option | description | default |
 | :--- | :--- | :--- |
@@ -164,11 +166,27 @@ This section covers all the available commands in 
SkyWalking CLI and their usage
 
 </details>
 
-### `single-metrics`
+#### `metrics multiple-linear`
 
 <details>
 
-<summary>single-metrics [--start=start-time] [--end=end-time] 
--name=metrics-name [--ids=entity-ids]</summary>
+<summary>metrics multiple-linear [--start=start-time] [--end=end-time] 
--name=metrics-name [--id=entity-id] [--num=number-of-linear-metrics]</summary>
+
+| option | description | default |
+| :--- | :--- | :--- |
+| `--name` | Metrics name, defined in 
[OAL](https://github.com/apache/skywalking/blob/master/oap-server/server-bootstrap/src/main/resources/official_analysis.oal),
 such as `all_p99`, etc. |
+| `--id` | the related id if the metrics requires one, e.g. for metrics 
`service_p99`, the service `id` is required, use `--id` to specify the service 
id, the same for `instance`, `endpoint`, etc. |
+| `--start` | See [Common options](#common-options) | See [Common 
options](#common-options) |
+| `--end` | See [Common options](#common-options) | See [Common 
options](#common-options) |
+| `--num` | Number of the linear metrics to fetch | `5` |
+
+</details>
+
+#### `metrics single`
+
+<details>
+
+<summary>metrics single [--start=start-time] [--end=end-time] 
--name=metrics-name [--ids=entity-ids]</summary>
 
 | option | description | default |
 | :--- | :--- | :--- |
@@ -239,7 +257,7 @@ otherwise,
 If you have already got the `id` of the instance:
 
 ```shell
-$ ./bin/swctl --display=graph linear-metrics --name=service_instance_resp_time 
--id 5
+$ ./bin/swctl --display=graph metrics linear --name=service_instance_resp_time 
--id 5
 
┌─────────────────────────────────────────────────────────────────────────────────Press
 q to 
quit──────────────────────────────────────────────────────────────────────────────────┐
 │                                                                              
                                                                                
                    │
 │                                                                              
                                                                                
                    │
@@ -263,7 +281,7 @@ $ ./bin/swctl --display=graph linear-metrics 
--name=service_instance_resp_time -
 otherwise
 
 ```shell
-$ ./bin/swctl instance ls --service-name=projectC | jq '.[] | select(.name == 
"projectC-pid:7895@skywalking-server-0001").id' | xargs ./bin/swctl 
--display=graph linear-metrics --name=service_instance_resp_time --id
+$ ./bin/swctl instance ls --service-name=projectC | jq '.[] | select(.name == 
"projectC-pid:7895@skywalking-server-0001").id' | xargs ./bin/swctl 
--display=graph metrics linear --name=service_instance_resp_time --id
 
┌─────────────────────────────────────────────────────────────────────────────────Press
 q to 
quit──────────────────────────────────────────────────────────────────────────────────┐
 │                                                                              
                                                                                
                    │
 │                                                                              
                                                                                
                    │
@@ -291,7 +309,7 @@ $ ./bin/swctl instance ls --service-name=projectC | jq '.[] 
| select(.name == "p
 <summary>Query a single metrics value for a specific endpoint</summary>
 
 ```shell
-$ ./bin/swctl service ls projectC | jq '.[0].id' | xargs ./bin/swctl endpoint 
ls --service-id | jq '.[] | [.id] | join(",")' | xargs ./bin/swctl 
single-metrics --name endpoint_cpm --ids
+$ ./bin/swctl service ls projectC | jq '.[0].id' | xargs ./bin/swctl endpoint 
ls --service-id | jq '.[] | [.id] | join(",")' | xargs ./bin/swctl metrics 
single --name endpoint_cpm --ids
 [{"id":"22","value":116}]
 ```
 
@@ -302,7 +320,7 @@ $ ./bin/swctl service ls projectC | jq '.[0].id' | xargs 
./bin/swctl endpoint ls
 <summary>Query metrics single values for all endpoints of service of id 
3</summary>
 
 ```shell
-$ ./bin/swctl service ls projectC | jq '.[0].id' | xargs ./bin/swctl endpoint 
ls --service-id | jq '.[] | [.id] | join(",")' | xargs ./bin/swctl 
single-metrics --name endpoint_cpm --end='2019-12-02 2137' --ids
+$ ./bin/swctl service ls projectC | jq '.[0].id' | xargs ./bin/swctl endpoint 
ls --service-id | jq '.[] | [.id] | join(",")' | xargs ./bin/swctl metrics 
single --name endpoint_cpm --end='2019-12-02 2137' --ids
 [{"id":"3","value":116}]
 ```
 
@@ -310,6 +328,73 @@ $ ./bin/swctl service ls projectC | jq '.[0].id' | xargs 
./bin/swctl endpoint ls
 
 <details>
 
+<summary>Query multiple metrics values for all percentiles</summary>
+
+```shell
+$ ./bin/swctl-latest-darwin-amd64 --display=graph --debug metrics 
multiple-linear --name all_percentile
+
+┌PRESS Q TO 
QUIT───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│┌───────────────────────────────#0───────────────────────────────┐┌───────────────────────────────#1───────────────────────────────┐┌─────────────────────────────────#2─────────────────────────────────┐│
+││      │  ⡏⠉⠉⢹   ⢸⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⡇      ⢸⠉⠉⠉⠉⠉⠉⠉⡇  ⢸⠉⠉⠉⠉⠉⠉⠉⡇   ⡏⠉⠉⠉ ││       │   
  ⢸⡀                       ⢸        ⢸        ⡇       ││        │                
                                  ⡠⠔⡇      ││
+││960.80│ ⢀⠇  ⠘⡄  ⡜            ⢣      ⢸       ⢇  ⢸       ⡇   ⡇    ││1963.60│   
  ⡜⡇                       ⢸        ⢸       ⢠⡇       ││ 2600.40│                
                                  ⡇ ⢣      ││
+││      │ ⢸    ⡇  ⡇            ⢸      ⢸       ⢸  ⡜       ⢸  ⢸     ││       │   
  ⡇⢸                       ⡼⡀       ⣾       ⢸⢣       ││        │                
                                 ⢸  ⢸      ││
+││      │ ⢸    ⡇  ⡇            ⢸      ⡸       ⢸  ⡇       ⢸  ⢸     ││       │   
  ⡇⠈⡆                      ⡇⡇       ⡇⡇      ⢸⢸       ││        │                
                                 ⢸  ⢸      ││
+││      │ ⢸    ⢣ ⢠⠃            ⠘⡄     ⡇       ⢸  ⡇       ⢸  ⢸     ││       │   
 ⢰⠁ ⡇                      ⡇⡇  ⡤⢤   ⡇⡇      ⡇⢸       ││        │                
                                 ⡇  ⠘⡄     ││
+││824.64│ ⡇    ⢸ ⢸              ⡇     ⡇       ⠈⡆ ⡇       ⠘⡄ ⡜     ││1832.88│   
 ⢸  ⢣                      ⡇⡇  ⡇⢸   ⡇⡇      ⡇⢸       ││ 2486.33│                
                                 ⡇   ⡇     ││
+││      │ ⡇    ⢸ ⢸              ⡇     ⡇        ⡇ ⡇        ⡇ ⡇     ││       │   
 ⢸  ⢸                      ⡇⡇ ⢸ ⠈⡆ ⢀⠇⡇     ⢠⠃⢸       ││        │                
                                ⢰⠁   ⡇     ││
+││      │ ⡇    ⠈⡆⡎              ⢣     ⡇        ⡇⢸         ⡇ ⡇     ││       │   
 ⡎  ⢸                     ⢰⠁⡇ ⢸  ⡇ ⢸ ⡇     ⢸ ⠘⡄      ││        │                
       ⡀        ⢸⠉⠲⡀  ⢀         ⢸    ⢱     ││
+││      │⢰⠁     ⡇⡇              ⢸     ⡇        ⢇⢸         ⡇ ⡇     ││       │   
 ⡇  ⢸                     ⢸ ⢱ ⢸  ⡇ ⢸ ⢣     ⢸  ⡇      ││        │⡀               
      ⢰⢱    ⢀⡄  ⡇  ⢱ ⢀⠎⡆        ⡎    ⢸  ⣀⠤ ││
+││688.48│⢸      ⡇⡇              ⢸     ⡇        ⢸⢸         ⢸⢸      ││1702.16│   
 ⡇   ⡇                    ⢸ ⢸ ⡇  ⢣ ⢸ ⢸     ⡜  ⡇      ││ 2372.24│⠱⡀       ⡴⡀  ⢀  
     ⢠⠃⠈⡆  ⢀⠎⠸⡀⢠⠃   ⢣⠎ ⢸  ⣠    ⡠⠃    ⢸ ⢰⠁  ││
+││      │⢸      ⢱⠁              ⠘⡄    ⡇        ⢸⢸         ⢸⢸      ││       │   
⢸    ⡇                    ⢸ ⢸ ⡇  ⢸ ⢸ ⢸     ⡇  ⡇      ││        │ ⢣      ⡜ ⠱⡀⡠⠋⡆ 
    ⣀⠎  ⢱ ⡠⠊  ⢣⢸        ⢇⡔⠁⢣ ⣀⠔⠁     ⠈⣦⠃   ││
+││      │⡜      ⠸                ⡇   ⢸         ⢸⡜         ⢸⢸      ││       │   
⢸    ⡇       ⡆     ⢀⡆     ⢸ ⢸⢀⠇  ⢸ ⡎ ⢸     ⡇  ⡇      ││        │  ⡇   ⡔⠊   ⠑⠁ 
⠸⡀  ⢠⠋    ⠈⠖⠁   ⠈⠇        ⠈   ⠉         ⠏    ││
+││      │⡇                       ⢣   ⢸         ⠈⡇         ⠘⡜      ││       │   
⡜    ⢱      ⢠⢣  ⢰⢄ ⡜⢸     ⡇ ⢸⢸   ⢸ ⡇ ⢸    ⢠⠃  ⢱      ││        │  ⢇   ⡇        
⢣⡀ ⡎                                        ││
+││552.32│⠁                       ⠸⡀  ⢸          ⡇          ⡇      ││1571.44│   
⡇    ⢸      ⢸⢸  ⡸ ⠙ ⠘⡄    ⡇ ⠘⣼    ⡇⡇ ⢸    ⢸   ⢸      ││ 2258.16│  ⢸  ⢸          
⠈⠙                                         ││
+││      │                         ⢇  ⢸                     ⠁      ││       │  
⢀⠇    ⢸      ⡜⢸  ⡇    ⢇    ⡇  ⡿    ⡇⡇  ⡇   ⢸   ⢸      ││        │  ⢸  ⢸         
                                            ││
+││      │                         ⢸  ⢸                            ││       │⢣ 
⢸     ⠸⡀     ⡇ ⡇ ⡇    ⢸    ⡇  ⡇    ⣇⠇  ⡇   ⡜   ⢸      ││        │  ⠈⡆ ⡜         
                                            ││
+││      │                          ⡇ ⢸                            ││       
│⠈⢆⡸      ⡇⢀   ⢠⠃ ⡇⢀⠇    ⠈⡦⠔⢇⢀⠇  ⠁    ⢹   ⡇   ⡇   ⢸      ││        │   ⡇ ⡇      
                                               ││
+││416.16│                          ⢱ ⢸                            ││1440.72│ 
⠘⡇      ⠋⠙⡄  ⢸  ⢱⢸        ⠸⣸        ⢸   ⠱⡀  ⡇   ⠈⡆     ││2144.080│   ⡇ ⡇        
                                             ││
+││      │                          ⠘⡄⡎                            ││       │   
        ⢇  ⡎  ⢸⢸         ⢿             ⠱⡀⢠⠃    ⡇     ││        │   ⢸⢸           
                                           ││
+││      │                           ⡇⡇                            ││       │   
        ⢸ ⢰⠁  ⠸⡜         ⠈              ⠘⣼     ⠧⣀    ││        │   ⢸⢸           
                                           ││
+││      │                           ⢸⡇                            ││       │   
         ⡇⡎    ⡇                         ⠈       ⠑⢄  ││        │   ⠘⡜           
                                           ││
+││   280│                           ⠈⡇                            ││   1310│   
         ⢱⠁                                          ││    2030│    ⡇           
                                           ││
+││      └─────────────────────────────────────────────────────────││       
└────────────────────────────────────────────────────────││        
└───────────────────────────────────────────────────────────││
+││       2020-03-07 0111   2020-03-07 0134   2020-03-07 0133      ││        
2020-03-07 0116   2020-03-07 0121   2020-03-07 0122     ││         2020-03-07 
0123   2020-03-07 0139   2020-03-07 0117        ││
+│└────────────────────────────────────────────────────────────────┘└────────────────────────────────────────────────────────────────┘└────────────────────────────────────────────────────────────────────┘│
+│┌────────────────────────────────────────────────#3─────────────────────────────────────────────────┐┌────────────────────────────────────────────────#4─────────────────────────────────────────────────┐│
+││       │                                           ⢀⢇                        
                      ││        │⠤⠤⠤⠤⠤⠤⡄     ⡤⠤⢤        
⢸⠑⠒⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠒⠊⠉⠉⠉⠉⠉⠉⠉⠒⠢⡄     ⡤⠒⠊⡇       ⢠⠔⠒⢹           ⢠⠔⠒⠉⠑⠢⠄ ││
+││       │                                           ⡸⠸⡀               ⢀⡆      
                      ││        │      ⡇     ⡇ ⢸        ⡸                       
   ⡇     ⡇  ⢇       ⢸  ⢸           ⢸       ││
+││3559.60│                                          ⢀⠇ ⢇              ⢀⠎⢸      
                      ││54073.20│      ⢱    ⢰⠁ ⠈⡆       ⡇                       
   ⢱    ⢰⠁  ⢸       ⡜   ⡇          ⡎       ││
+││       │           ⢀⢄                             ⡸  ⠸⡀            ⢀⠎ ⠘⡄     
                      ││        │      ⢸    ⢸   ⡇       ⡇                       
   ⢸    ⢸   ⢸       ⡇   ⡇          ⡇       ││
+││       │          ⢀⠎ ⠑⢄                          ⢀⠇   ⢇           ⢀⠎   ⡇     
    ⣼                 ││        │      ⢸    ⢸   ⡇       ⡇                       
   ⢸    ⢸   ⢸       ⡇   ⡇          ⡇       ││
+││       │         ⢀⠎   ⠈⢆                      ⣀  ⡸    ⠸⡀ ⣀⡀       ⡜    ⢸     
   ⡸⠸⡀                ││        │      ⠸⡀   ⡸   ⢇      ⢰⠁                       
   ⠸⡀   ⡸   ⠈⡆      ⡇   ⢣         ⢀⠇       ││
+││3325.68│   ⣀⣀  ⣀⠤⠊     ⠘⡄   ⢀⣀⣀⣀⣀⡠⠤⡀      ⢀⣀⠔⠊ ⠉⠑⠃     ⠉⠉ ⠘⢄     ⡰⠁    ⠘⡄    
  ⢰⠁ ⡇       ⢀⣀⡠⠤⠤⠤⠄  ││43924.56│       ⡇   ⡇   ⢸      ⢸                        
    ⡇   ⡇    ⡇     ⢰⠁   ⢸         ⢸        ││
+││       │ ⢠⠊  ⠉⠉         ⠸⡀ ⡔⠁      ⠑⢄  ⡠⠊⠉⠁                 ⠣⣀  ⢠⠃      ⡇    
 ⢠⠃  ⡇    ⢀⠤⠊⠁        ││        │       ⡇   ⡇   ⢸      ⢸                        
    ⡇   ⡇    ⡇     ⢸    ⢸         ⢸        ││
+││       │⠔⠁               ⠱⠊         ⠈⠢⠊                       ⠉⠒⠎       
⠸⠤⠤⠤⠔⠊⠁   ⢇    ⢸           ││        │       ⡇   ⡇   ⢸      ⡸                   
         ⡇   ⡇    ⢇     ⢸    ⢸         ⢸        ││
+││       │                                                                     
     ⢸    ⡎           ││        │       ⢱  ⢰⠁   ⠈⡆     ⡇                        
    ⢸  ⢸     ⢸     ⢸     ⡇        ⡎        ││
+││3091.76│                                                                     
     ⢸    ⡇           ││33775.92│       ⢸  ⢸     ⡇     ⡇                        
    ⢸  ⢸     ⢸     ⡇     ⡇        ⡇        ││
+││       │                                                                     
     ⢸   ⢀⠇           ││        │       ⢸  ⢸     ⡇     ⡇                        
    ⢸  ⢸     ⢸     ⡇     ⡇        ⡇        ││
+││       │                                                                     
      ⡇  ⢸            ││        │       ⠸⡀ ⡸     ⢇    ⢰⠁                        
    ⠘⡄ ⡜     ⠈⡆    ⡇     ⢣       ⢠⠃        ││
+││       │                                                                     
      ⡇  ⢸            ││        │        ⡇ ⡇     ⢸    ⢸                         
     ⡇ ⡇      ⡇   ⢠⠃     ⢸       ⢸         ││
+││2857.84│                                                                     
      ⡇  ⡎            ││23627.28│        ⡇ ⡇     ⢸    ⢸                         
     ⡇ ⡇      ⡇   ⢸      ⢸       ⢸         ││
+││       │                                                                     
      ⢸  ⡇            ││        │        ⡇ ⡇     ⢸    ⡸                         
     ⢇⢀⠇      ⢇   ⢸      ⢸       ⢸         ││
+││       │                                                                     
      ⢸ ⢀⠇            ││        │        ⢱⢰⠁     ⠈⡆   ⡇                         
     ⢸⢸       ⢸   ⢸       ⡇      ⡇         ││
+││       │                                                                     
      ⢸ ⢸             ││        │        ⢸⢸       ⡇   ⡇                         
     ⢸⢸       ⢸   ⡎       ⡇      ⡇         ││
+││2623.92│                                                                     
      ⠈⡆⢸             ││13478.64│        ⢸⢸       ⡇   ⡇                         
     ⢸⢸       ⢸   ⡇       ⡇      ⡇         ││
+││       │                                                                     
       ⡇⡎             ││        │        ⠸⡸       ⢇  ⢰⠁                         
     ⠈⡎       ⠈⡆  ⡇       ⢣     ⢠⠃         ││
+││       │                                                                     
       ⡇⡇             ││        │         ⡇       ⢸  ⣸                          
      ⡇        ⡇  ⡇       ⢸     ⢸          ││
+││       │                                                                     
       ⢱⠇             ││        │         ⠃       ⠘⠊⠉                           
               ⠘⡄⢸        ⠘⠒⠊⠉⠉⠉⠉          ││
+││   2390│                                                                     
       ⢸              ││    3330│                                               
                ⠈⢾                         ││
+││       
└───────────────────────────────────────────────────────────────────────────────────────────││
        
└──────────────────────────────────────────────────────────────────────────────────────────││
+││        2020-03-07 0115   2020-03-07 0139   2020-03-07 0134   2020-03-07 
0136   2020-03-07 0132    ││         2020-03-07 0115   2020-03-07 0126   
2020-03-07 0112   2020-03-07 0134   2020-03-07 0124   ││
+│└───────────────────────────────────────────────────────────────────────────────────────────────────┘└───────────────────────────────────────────────────────────────────────────────────────────────────┘│
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+
+```
+
+</details>
+
+<details>
+
 <summary>Automatically convert to server side timezone</summary>
 
 if your backend nodes are deployed in docker and the timezone is UTC, you may 
not want to convert your timezone to UTC every time you type a command, 
`--timezone` comes to your rescue.
diff --git a/commands/endpoint/list.go b/commands/endpoint/list.go
index 51ea320..034c6a5 100644
--- a/commands/endpoint/list.go
+++ b/commands/endpoint/list.go
@@ -20,8 +20,9 @@ package endpoint
 import (
        "github.com/urfave/cli"
 
+       "github.com/apache/skywalking-cli/graphql/metadata"
+
        "github.com/apache/skywalking-cli/display"
-       "github.com/apache/skywalking-cli/graphql/client"
 )
 
 var ListCommand = cli.Command{
@@ -53,7 +54,7 @@ var ListCommand = cli.Command{
                limit := ctx.Int("limit")
                keyword := ctx.String("keyword")
 
-               endpoints := client.SearchEndpoints(ctx, serviceID, keyword, 
limit)
+               endpoints := metadata.SearchEndpoints(ctx, serviceID, keyword, 
limit)
 
                return display.Display(ctx, endpoints)
        },
diff --git a/commands/instance/instance.go b/commands/instance/instance.go
index da83c80..29c86a7 100644
--- a/commands/instance/instance.go
+++ b/commands/instance/instance.go
@@ -20,7 +20,8 @@ package instance
 import (
        "github.com/urfave/cli"
 
-       "github.com/apache/skywalking-cli/graphql/client"
+       "github.com/apache/skywalking-cli/graphql/metadata"
+
        "github.com/apache/skywalking-cli/logger"
 )
 
@@ -43,7 +44,7 @@ func verifyAndSwitchServiceParameter(ctx *cli.Context) string 
{
        }
 
        if serviceID == "" && serviceName != "" {
-               service, err := client.SearchService(ctx, serviceName)
+               service, err := metadata.SearchService(ctx, serviceName)
                if err != nil {
                        logger.Log.Fatalln(err)
                }
diff --git a/commands/instance/list.go b/commands/instance/list.go
index c20ccb4..eda72c1 100644
--- a/commands/instance/list.go
+++ b/commands/instance/list.go
@@ -20,11 +20,12 @@ package instance
 import (
        "github.com/urfave/cli"
 
+       "github.com/apache/skywalking-cli/graphql/metadata"
+
        "github.com/apache/skywalking-cli/commands/flags"
        "github.com/apache/skywalking-cli/commands/interceptor"
        "github.com/apache/skywalking-cli/commands/model"
        "github.com/apache/skywalking-cli/display"
-       "github.com/apache/skywalking-cli/graphql/client"
        "github.com/apache/skywalking-cli/graphql/schema"
 )
 
@@ -44,7 +45,7 @@ var ListCommand = cli.Command{
                start := ctx.String("start")
                step := ctx.Generic("step")
 
-               instances := client.Instances(ctx, serviceID, schema.Duration{
+               instances := metadata.Instances(ctx, serviceID, schema.Duration{
                        Start: start,
                        End:   end,
                        Step:  step.(*model.StepEnumValue).Selected,
diff --git a/commands/instance/search.go b/commands/instance/search.go
index b9e2ac9..b97adae 100644
--- a/commands/instance/search.go
+++ b/commands/instance/search.go
@@ -20,13 +20,14 @@ package instance
 import (
        "regexp"
 
+       "github.com/apache/skywalking-cli/graphql/metadata"
+
        "github.com/urfave/cli"
 
        "github.com/apache/skywalking-cli/commands/flags"
        "github.com/apache/skywalking-cli/commands/interceptor"
        "github.com/apache/skywalking-cli/commands/model"
        "github.com/apache/skywalking-cli/display"
-       "github.com/apache/skywalking-cli/graphql/client"
        "github.com/apache/skywalking-cli/graphql/schema"
 )
 
@@ -47,7 +48,7 @@ var SearchCommand = cli.Command{
 
                regex := ctx.String("regex")
 
-               instances := client.Instances(ctx, serviceID, schema.Duration{
+               instances := metadata.Instances(ctx, serviceID, schema.Duration{
                        Start: start,
                        End:   end,
                        Step:  step.(*model.StepEnumValue).Selected,
diff --git a/commands/interceptor/duration.go b/commands/interceptor/duration.go
index 3eb355a..c52ae69 100644
--- a/commands/interceptor/duration.go
+++ b/commands/interceptor/duration.go
@@ -21,6 +21,8 @@ import (
        "strconv"
        "time"
 
+       "github.com/apache/skywalking-cli/graphql/utils"
+
        "github.com/urfave/cli"
 
        "github.com/apache/skywalking-cli/graphql/schema"
@@ -29,7 +31,7 @@ import (
 
 func tryParseTime(unparsed string) (schema.Step, time.Time, error) {
        var possibleError error = nil
-       for step, layout := range schema.StepFormats {
+       for step, layout := range utils.StepFormats {
                t, err := time.Parse(layout, unparsed)
                if err == nil {
                        return step, t, nil
@@ -48,9 +50,9 @@ func DurationInterceptor(ctx *cli.Context) error {
 
        startTime, endTime, step := ParseDuration(start, end, timezone)
 
-       if err := ctx.Set("start", startTime.Format(schema.StepFormats[step])); 
err != nil {
+       if err := ctx.Set("start", startTime.Format(utils.StepFormats[step])); 
err != nil {
                return err
-       } else if err := ctx.Set("end", 
endTime.Format(schema.StepFormats[step])); err != nil {
+       } else if err := ctx.Set("end", 
endTime.Format(utils.StepFormats[step])); err != nil {
                return err
        } else if err := ctx.Set("step", step.String()); err != nil {
                return err
@@ -103,12 +105,12 @@ func ParseDuration(start, end, timezone string) 
(startTime, endTime time.Time, s
                if step, startTime, err = tryParseTime(start); err != nil {
                        logger.Log.Fatalln("Unsupported time format:", start, 
err)
                }
-               return startTime, startTime.Add(30 * 
schema.StepDuration[step]), step
+               return startTime, startTime.Add(30 * utils.StepDuration[step]), 
step
        } else { // start is absent
                if step, endTime, err = tryParseTime(end); err != nil {
                        logger.Log.Fatalln("Unsupported time format:", end, err)
                }
-               return endTime.Add(-30 * schema.StepDuration[step]), endTime, 
step
+               return endTime.Add(-30 * utils.StepDuration[step]), endTime, 
step
        }
 }
 
diff --git a/commands/metrics/linear/linear-metrics.go 
b/commands/metrics/linear/linear-metrics.go
index ceda447..d435063 100644
--- a/commands/metrics/linear/linear-metrics.go
+++ b/commands/metrics/linear/linear-metrics.go
@@ -20,16 +20,18 @@ package linear
 import (
        "github.com/urfave/cli"
 
+       "github.com/apache/skywalking-cli/graphql/metrics"
+       "github.com/apache/skywalking-cli/graphql/utils"
+
        "github.com/apache/skywalking-cli/commands/flags"
        "github.com/apache/skywalking-cli/commands/interceptor"
        "github.com/apache/skywalking-cli/commands/model"
        "github.com/apache/skywalking-cli/display"
-       "github.com/apache/skywalking-cli/graphql/client"
        "github.com/apache/skywalking-cli/graphql/schema"
 )
 
-var Command = cli.Command{
-       Name:  "linear-metrics",
+var Single = cli.Command{
+       Name:  "linear",
        Usage: "Query linear metrics defined in backend OAL",
        Flags: flags.Flags(
                flags.DurationFlags,
@@ -62,15 +64,17 @@ var Command = cli.Command{
                        id = &idString
                }
 
-               metricsValues := client.LinearIntValues(ctx, 
schema.MetricCondition{
-                       Name: metricsName,
-                       ID:   id,
-               }, schema.Duration{
+               duration := schema.Duration{
                        Start: start,
                        End:   end,
                        Step:  step.(*model.StepEnumValue).Selected,
-               })
+               }
+
+               metricsValues := metrics.LinearIntValues(ctx, 
schema.MetricCondition{
+                       Name: metricsName,
+                       ID:   id,
+               }, duration)
 
-               return display.Display(ctx, metricsValues)
+               return display.Display(ctx, utils.MetricsToMap(duration, 
metricsValues))
        },
 }
diff --git a/commands/metrics/linear/linear-metrics.go 
b/commands/metrics/linear/multiple-linear-metrics.go
similarity index 70%
copy from commands/metrics/linear/linear-metrics.go
copy to commands/metrics/linear/multiple-linear-metrics.go
index ceda447..31db251 100644
--- a/commands/metrics/linear/linear-metrics.go
+++ b/commands/metrics/linear/multiple-linear-metrics.go
@@ -20,23 +20,25 @@ package linear
 import (
        "github.com/urfave/cli"
 
+       "github.com/apache/skywalking-cli/graphql/metrics"
+       "github.com/apache/skywalking-cli/graphql/utils"
+
        "github.com/apache/skywalking-cli/commands/flags"
        "github.com/apache/skywalking-cli/commands/interceptor"
        "github.com/apache/skywalking-cli/commands/model"
        "github.com/apache/skywalking-cli/display"
-       "github.com/apache/skywalking-cli/graphql/client"
        "github.com/apache/skywalking-cli/graphql/schema"
 )
 
-var Command = cli.Command{
-       Name:  "linear-metrics",
-       Usage: "Query linear metrics defined in backend OAL",
+var Multiple = cli.Command{
+       Name:  "multiple-linear",
+       Usage: "Query multiple linear metrics defined in backend OAL",
        Flags: flags.Flags(
                flags.DurationFlags,
                []cli.Flag{
                        cli.StringFlag{
                                Name:     "name",
-                               Usage:    "metrics `NAME`, such as `all_p99`",
+                               Usage:    "metrics `NAME`, such as 
`all_percentile`",
                                Required: true,
                        },
                        cli.StringFlag{
@@ -44,6 +46,12 @@ var Command = cli.Command{
                                Usage:    "`ID`, the related id if the metrics 
requires one",
                                Required: false,
                        },
+                       cli.IntFlag{
+                               Name:     "num",
+                               Usage:    "`num`, the number of linear metrics 
to query, (default: 5)",
+                               Required: false,
+                               Value:    5,
+                       },
                },
        ),
        Before: interceptor.BeforeChain([]cli.BeforeFunc{
@@ -55,6 +63,7 @@ var Command = cli.Command{
                start := ctx.String("start")
                step := ctx.Generic("step")
                metricsName := ctx.String("name")
+               numOfLinear := ctx.Int("num")
 
                var id *string = nil
 
@@ -62,15 +71,23 @@ var Command = cli.Command{
                        id = &idString
                }
 
-               metricsValues := client.LinearIntValues(ctx, 
schema.MetricCondition{
-                       Name: metricsName,
-                       ID:   id,
-               }, schema.Duration{
+               duration := schema.Duration{
                        Start: start,
                        End:   end,
                        Step:  step.(*model.StepEnumValue).Selected,
-               })
+               }
+
+               values := metrics.MultipleLinearIntValues(ctx, 
schema.MetricCondition{
+                       Name: metricsName,
+                       ID:   id,
+               }, numOfLinear, duration)
+
+               reshaped := make([]map[string]float64, len(values))
+
+               for index, value := range values {
+                       reshaped[index] = utils.MetricsToMap(duration, value)
+               }
 
-               return display.Display(ctx, metricsValues)
+               return display.Display(ctx, reshaped)
        },
 }
diff --git a/display/graph/graph.go b/commands/metrics/metrics.go
similarity index 69%
copy from display/graph/graph.go
copy to commands/metrics/metrics.go
index 7184360..8f941b9 100644
--- a/display/graph/graph.go
+++ b/commands/metrics/metrics.go
@@ -15,21 +15,21 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package graph
+package metrics
 
 import (
-       "fmt"
-       "reflect"
+       "github.com/urfave/cli"
 
-       "github.com/apache/skywalking-cli/display/graph/linear"
+       "github.com/apache/skywalking-cli/commands/metrics/linear"
+       "github.com/apache/skywalking-cli/commands/metrics/single"
 )
 
-func Display(object interface{}) error {
-       if reflect.TypeOf(object) != reflect.TypeOf(map[string]float64{}) {
-               return fmt.Errorf("type of %T is not supported to be displayed 
as ascii graph", reflect.TypeOf(object))
-       }
-
-       kvs := object.(map[string]float64)
-
-       return linear.Display(kvs)
+var Command = cli.Command{
+       Name:  "metrics",
+       Usage: "Query metrics defined in backend OAL",
+       Subcommands: cli.Commands{
+               single.Command,
+               linear.Single,
+               linear.Multiple,
+       },
 }
diff --git a/commands/metrics/single/single-metrics.go 
b/commands/metrics/single/single-metrics.go
index 99817da..69405ea 100644
--- a/commands/metrics/single/single-metrics.go
+++ b/commands/metrics/single/single-metrics.go
@@ -20,18 +20,19 @@ package single
 import (
        "strings"
 
+       "github.com/apache/skywalking-cli/graphql/metrics"
+
        "github.com/urfave/cli"
 
        "github.com/apache/skywalking-cli/commands/flags"
        "github.com/apache/skywalking-cli/commands/interceptor"
        "github.com/apache/skywalking-cli/commands/model"
        "github.com/apache/skywalking-cli/display"
-       "github.com/apache/skywalking-cli/graphql/client"
        "github.com/apache/skywalking-cli/graphql/schema"
 )
 
 var Command = cli.Command{
-       Name:  "single-metrics",
+       Name:  "single",
        Usage: "Query single metrics defined in backend OAL",
        Flags: flags.Flags(
                flags.DurationFlags,
@@ -65,7 +66,7 @@ var Command = cli.Command{
                        ids = append(ids, strings.Split(id, ",")...)
                }
 
-               metricsValues := client.IntValues(ctx, 
schema.BatchMetricConditions{
+               metricsValues := metrics.IntValues(ctx, 
schema.BatchMetricConditions{
                        Name: metricsName,
                        Ids:  ids,
                }, schema.Duration{
@@ -74,6 +75,6 @@ var Command = cli.Command{
                        Step:  step.(*model.StepEnumValue).Selected,
                })
 
-               return display.Display(ctx, metricsValues)
+               return display.Display(ctx, metricsValues.Values)
        },
 }
diff --git a/commands/service/list.go b/commands/service/list.go
index 8400a47..2604a6f 100644
--- a/commands/service/list.go
+++ b/commands/service/list.go
@@ -20,11 +20,12 @@ package service
 import (
        "github.com/urfave/cli"
 
+       "github.com/apache/skywalking-cli/graphql/metadata"
+
        "github.com/apache/skywalking-cli/commands/flags"
        "github.com/apache/skywalking-cli/commands/interceptor"
        "github.com/apache/skywalking-cli/commands/model"
        "github.com/apache/skywalking-cli/display"
-       "github.com/apache/skywalking-cli/graphql/client"
        "github.com/apache/skywalking-cli/graphql/schema"
 )
 
@@ -47,13 +48,13 @@ var ListCommand = cli.Command{
                var services []schema.Service
 
                if args := ctx.Args(); len(args) == 0 {
-                       services = client.Services(ctx, schema.Duration{
+                       services = metadata.AllServices(ctx, schema.Duration{
                                Start: start,
                                End:   end,
                                Step:  step.(*model.StepEnumValue).Selected,
                        })
                } else {
-                       service, _ := client.SearchService(ctx, args.First())
+                       service, _ := metadata.SearchService(ctx, args.First())
                        services = []schema.Service{service}
                }
 
diff --git a/display/display.go b/display/display.go
index 48be6ab..e620673 100644
--- a/display/display.go
+++ b/display/display.go
@@ -31,10 +31,10 @@ import (
 )
 
 const (
-       JSON  string = "json"
-       YAML  string = "yaml"
-       TABLE string = "table"
-       GRAPH string = "graph"
+       JSON  = "json"
+       YAML  = "yaml"
+       TABLE = "table"
+       GRAPH = "graph"
 )
 
 // Display the object in the style specified in flag --display
diff --git a/display/graph/graph.go b/display/graph/graph.go
index 7184360..c27f835 100644
--- a/display/graph/graph.go
+++ b/display/graph/graph.go
@@ -25,11 +25,17 @@ import (
 )
 
 func Display(object interface{}) error {
-       if reflect.TypeOf(object) != reflect.TypeOf(map[string]float64{}) {
-               return fmt.Errorf("type of %T is not supported to be displayed 
as ascii graph", reflect.TypeOf(object))
+       if reflect.TypeOf(object) == reflect.TypeOf(map[string]float64{}) {
+               kvs := []map[string]float64{object.(map[string]float64)}
+
+               return linear.Display(kvs)
        }
 
-       kvs := object.(map[string]float64)
+       if reflect.TypeOf(object) == reflect.TypeOf([]map[string]float64{}) {
+               kvs := object.([]map[string]float64)
+
+               return linear.Display(kvs)
+       }
 
-       return linear.Display(kvs)
+       return fmt.Errorf("type of %T is not supported to be displayed as ascii 
graph", reflect.TypeOf(object))
 }
diff --git a/display/graph/linear/linear.go b/display/graph/linear/linear.go
index b6e6d07..c632112 100644
--- a/display/graph/linear/linear.go
+++ b/display/graph/linear/linear.go
@@ -19,14 +19,15 @@ package linear
 
 import (
        "context"
+       "fmt"
+       "math"
        "strings"
 
-       "github.com/mum4k/termdash/widgetapi"
+       "github.com/mum4k/termdash/linestyle"
 
        "github.com/mum4k/termdash"
        "github.com/mum4k/termdash/container"
        "github.com/mum4k/termdash/container/grid"
-       "github.com/mum4k/termdash/linestyle"
        "github.com/mum4k/termdash/terminal/termbox"
        "github.com/mum4k/termdash/terminal/terminalapi"
        "github.com/mum4k/termdash/widgets/linechart"
@@ -34,20 +35,19 @@ import (
 
 const RootID = "root"
 
-func newWidgets(inputs map[string]float64) (lineChart *linechart.LineChart, 
err error) {
+func newLineChart(inputs map[string]float64) (lineChart *linechart.LineChart, 
err error) {
        index := 0
 
        xLabels := map[int]string{}
-       var yValues []float64
+       yValues := make([]float64, len(inputs))
+
        for xLabel, yValue := range inputs {
                xLabels[index] = xLabel
+               yValues[index] = yValue
                index++
-               yValues = append(yValues, yValue)
        }
 
-       if lineChart, err = linechart.New(
-               linechart.YAxisAdaptive(),
-       ); err != nil {
+       if lineChart, err = linechart.New(linechart.YAxisAdaptive()); err != 
nil {
                return
        }
 
@@ -56,21 +56,42 @@ func newWidgets(inputs map[string]float64) (lineChart 
*linechart.LineChart, err
        return lineChart, err
 }
 
-func gridLayout(lineChart widgetapi.Widget) ([]container.Option, error) {
-       widget := grid.Widget(
-               lineChart,
-               container.Border(linestyle.Light),
-               container.BorderTitleAlignCenter(),
-               container.BorderTitle("Press q to quit"),
-       )
+func layout(lineCharts ...*linechart.LineChart) ([]container.Option, error) {
+       cols := maxSqrt(len(lineCharts))
+
+       rows := make([][]grid.Element, 
int(math.Ceil(float64(len(lineCharts))/float64(cols))))
+
+       for r := 0; r < len(rows); r++ {
+               var row []grid.Element
+               for c := 0; c < cols && r*cols+c < len(lineCharts); c++ {
+                       percentage := int(math.Floor(float64(100) / 
float64(cols)))
+                       if r == len(rows)-1 {
+                               percentage = int(math.Floor(float64(100) / 
float64(len(lineCharts)-r*cols)))
+                       }
+                       row = append(row, grid.ColWidthPerc(
+                               int(math.Min(99, float64(percentage))),
+                               grid.Widget(
+                                       lineCharts[r*cols+c],
+                                       container.Border(linestyle.Light),
+                                       container.BorderTitleAlignCenter(),
+                                       
container.BorderTitle(fmt.Sprintf("#%v", r*cols+c)),
+                               ),
+                       ))
+               }
+               rows[r] = row
+       }
 
        builder := grid.New()
-       builder.Add(widget)
+
+       for _, row := range rows {
+               percentage := int(math.Min(99, float64(100/len(rows))))
+               builder.Add(grid.RowHeightPerc(percentage, row...))
+       }
 
        return builder.Build()
 }
 
-func Display(inputs map[string]float64) error {
+func Display(inputs []map[string]float64) error {
        t, err := termbox.New()
        if err != nil {
                return err
@@ -80,26 +101,31 @@ func Display(inputs map[string]float64) error {
        c, err := container.New(
                t,
                container.ID(RootID),
-               container.PaddingTop(2),
-               container.PaddingRight(2),
-               container.PaddingBottom(2),
-               container.PaddingLeft(2),
        )
        if err != nil {
                return err
        }
 
-       w, err := newWidgets(inputs)
-       if err != nil {
-               return err
+       var elements []*linechart.LineChart
+
+       for _, input := range inputs {
+               w, e := newLineChart(input)
+               if e != nil {
+                       return e
+               }
+               elements = append(elements, w)
        }
 
-       gridOpts, err := gridLayout(w)
+       gridOpts, err := layout(elements...)
        if err != nil {
                return err
        }
 
-       err = c.Update(RootID, gridOpts...)
+       err = c.Update(RootID, append(
+               gridOpts,
+               container.Border(linestyle.Light),
+               container.BorderTitle("PRESS Q TO QUIT"))...,
+       )
 
        if err != nil {
                return err
@@ -116,3 +142,7 @@ func Display(inputs map[string]float64) error {
 
        return err
 }
+
+func maxSqrt(num int) int {
+       return int(math.Ceil(math.Sqrt(float64(num))))
+}
diff --git a/graphql/client/client.go b/graphql/client/client.go
index a0766ef..a14f2f8 100644
--- a/graphql/client/client.go
+++ b/graphql/client/client.go
@@ -19,13 +19,10 @@ package client
 
 import (
        "context"
-       "fmt"
-       "time"
 
        "github.com/machinebox/graphql"
        "github.com/urfave/cli"
 
-       "github.com/apache/skywalking-cli/graphql/schema"
        "github.com/apache/skywalking-cli/logger"
 )
 
@@ -37,6 +34,7 @@ func newClient(cliCtx *cli.Context) (client *graphql.Client) {
        return
 }
 
+// ExecuteQuery executes the `request` and parse to the `response`, returning 
`error` if there is any.
 func ExecuteQuery(cliCtx *cli.Context, request *graphql.Request, response 
interface{}) error {
        client := newClient(cliCtx)
        ctx := context.Background()
@@ -44,151 +42,9 @@ func ExecuteQuery(cliCtx *cli.Context, request 
*graphql.Request, response interf
        return err
 }
 
+// ExecuteQuery executes the `request` and parse to the `response`, panic if 
there is any `error`.
 func ExecuteQueryOrFail(cliCtx *cli.Context, request *graphql.Request, 
response interface{}) {
-       client := newClient(cliCtx)
-       ctx := context.Background()
-       if err := client.Run(ctx, request, response); err != nil {
-               logger.Log.Fatalln(err)
-       }
-}
-
-func Services(cliCtx *cli.Context, duration schema.Duration) []schema.Service {
-       var response map[string][]schema.Service
-       request := graphql.NewRequest(`
-               query ($duration: Duration!) {
-                       services: getAllServices(duration: $duration) {
-                               id name
-                       }
-               }
-       `)
-       request.Var("duration", duration)
-
-       ExecuteQueryOrFail(cliCtx, request, &response)
-       return response["services"]
-}
-
-func SearchEndpoints(cliCtx *cli.Context, serviceID, keyword string, limit 
int) []schema.Endpoint {
-       var response map[string][]schema.Endpoint
-       request := graphql.NewRequest(`
-               query ($keyword: String!, $serviceId: ID!, $limit: Int!) {
-                       endpoints: searchEndpoint(keyword: $keyword, serviceId: 
$serviceId, limit: $limit) {
-                               id name
-                       }
-               }
-       `)
-       request.Var("serviceId", serviceID)
-       request.Var("keyword", keyword)
-       request.Var("limit", limit)
-
-       ExecuteQueryOrFail(cliCtx, request, &response)
-       return response["endpoints"]
-}
-
-func GetEndpointInfo(cliCtx *cli.Context, endpointID string) schema.Endpoint {
-       var response map[string]schema.Endpoint
-       request := graphql.NewRequest(`
-               query ($endpointId: ID!) {
-                       endpoint: getEndpointInfo(endpointId: $endpointId) {
-                               id name
-                       }
-               }
-       `)
-       request.Var("endpointId", endpointID)
-
-       ExecuteQueryOrFail(cliCtx, request, &response)
-       return response["endpoint"]
-}
-
-func Instances(cliCtx *cli.Context, serviceID string, duration 
schema.Duration) []schema.ServiceInstance {
-       var response map[string][]schema.ServiceInstance
-       request := graphql.NewRequest(`
-               query ($serviceId: ID!, $duration: Duration!) {
-                       instances: getServiceInstances(duration: $duration, 
serviceId: $serviceId) {
-                               id
-                               name
-                               language
-                               instanceUUID
-                               attributes {
-                                       name
-                                       value
-                               }
-                       }
-               }
-       `)
-       request.Var("serviceId", serviceID)
-       request.Var("duration", duration)
-
-       ExecuteQueryOrFail(cliCtx, request, &response)
-       return response["instances"]
-}
-
-func SearchService(cliCtx *cli.Context, serviceCode string) (service 
schema.Service, err error) {
-       var response map[string]schema.Service
-       request := graphql.NewRequest(`
-               query searchService($serviceCode: String!) {
-                       service: searchService(serviceCode: $serviceCode) {
-                               id name
-                       }
-               }
-       `)
-       request.Var("serviceCode", serviceCode)
-
-       ExecuteQueryOrFail(cliCtx, request, &response)
-       service = response["service"]
-       if service.ID == "" {
-               return service, fmt.Errorf("no such service [%s]", serviceCode)
-       }
-       return service, nil
-}
-
-func LinearIntValues(ctx *cli.Context, condition schema.MetricCondition, 
duration schema.Duration) map[string]float64 {
-       var response map[string]schema.IntValues
-
-       request := graphql.NewRequest(`
-               query ($metric: MetricCondition!, $duration: Duration!) {
-                       metrics: getLinearIntValues(metric: $metric, duration: 
$duration) {
-                               values { value }
-                       }
-               }
-       `)
-       request.Var("metric", condition)
-       request.Var("duration", duration)
-
-       ExecuteQueryOrFail(ctx, request, &response)
-
-       values := metricsToMap(duration, response["metrics"].Values)
-
-       return values
-}
-
-func IntValues(ctx *cli.Context, condition schema.BatchMetricConditions, 
duration schema.Duration) []*schema.KVInt {
-       var response map[string]schema.IntValues
-
-       request := graphql.NewRequest(`
-               query ($metric: BatchMetricConditions!, $duration: Duration!) {
-                       metrics: getValues(metric: $metric, duration: 
$duration) {
-                               values { id value }
-                       }
-               }
-       `)
-       request.Var("metric", condition)
-       request.Var("duration", duration)
-
-       ExecuteQueryOrFail(ctx, request, &response)
-
-       return response["metrics"].Values
-}
-
-func metricsToMap(duration schema.Duration, kvInts []*schema.KVInt) 
map[string]float64 {
-       values := map[string]float64{}
-       format := schema.StepFormats[duration.Step]
-       startTime, err := time.Parse(format, duration.Start)
-       if err != nil {
+       if err := ExecuteQuery(cliCtx, request, response); err != nil {
                logger.Log.Fatalln(err)
        }
-       step := schema.StepDuration[duration.Step]
-       for idx, value := range kvInts {
-               values[startTime.Add(time.Duration(idx)*step).Format(format)] = 
float64(value.Value)
-       }
-       return values
 }
diff --git a/graphql/metadata/metadata.go b/graphql/metadata/metadata.go
index 55a1bc7..93b6875 100644
--- a/graphql/metadata/metadata.go
+++ b/graphql/metadata/metadata.go
@@ -18,6 +18,8 @@
 package metadata
 
 import (
+       "fmt"
+
        "github.com/machinebox/graphql"
        "github.com/urfave/cli"
 
@@ -25,7 +27,97 @@ import (
        "github.com/apache/skywalking-cli/graphql/schema"
 )
 
+func AllServices(cliCtx *cli.Context, duration schema.Duration) 
[]schema.Service {
+       var response map[string][]schema.Service
+       request := graphql.NewRequest(`
+               query ($duration: Duration!) {
+                       services: getAllServices(duration: $duration) {
+                               id name
+                       }
+               }
+       `)
+       request.Var("duration", duration)
+
+       client.ExecuteQueryOrFail(cliCtx, request, &response)
+       return response["services"]
+}
+
+func SearchService(cliCtx *cli.Context, serviceCode string) (service 
schema.Service, err error) {
+       var response map[string]schema.Service
+       request := graphql.NewRequest(`
+               query searchService($serviceCode: String!) {
+                       service: searchService(serviceCode: $serviceCode) {
+                               id name
+                       }
+               }
+       `)
+       request.Var("serviceCode", serviceCode)
+
+       client.ExecuteQueryOrFail(cliCtx, request, &response)
+       service = response["service"]
+       if service.ID == "" {
+               return service, fmt.Errorf("no such service [%s]", serviceCode)
+       }
+       return service, nil
+}
+
+func SearchEndpoints(cliCtx *cli.Context, serviceID, keyword string, limit 
int) []schema.Endpoint {
+       var response map[string][]schema.Endpoint
+       request := graphql.NewRequest(`
+               query ($keyword: String!, $serviceId: ID!, $limit: Int!) {
+                       endpoints: searchEndpoint(keyword: $keyword, serviceId: 
$serviceId, limit: $limit) {
+                               id name
+                       }
+               }
+       `)
+       request.Var("serviceId", serviceID)
+       request.Var("keyword", keyword)
+       request.Var("limit", limit)
+
+       client.ExecuteQueryOrFail(cliCtx, request, &response)
+       return response["endpoints"]
+}
+
+func EndpointInfo(cliCtx *cli.Context, endpointID string) schema.Endpoint {
+       var response map[string]schema.Endpoint
+       request := graphql.NewRequest(`
+               query ($endpointId: ID!) {
+                       endpoint: getEndpointInfo(endpointId: $endpointId) {
+                               id name
+                       }
+               }
+       `)
+       request.Var("endpointId", endpointID)
+
+       client.ExecuteQueryOrFail(cliCtx, request, &response)
+       return response["endpoint"]
+}
+
+func Instances(cliCtx *cli.Context, serviceID string, duration 
schema.Duration) []schema.ServiceInstance {
+       var response map[string][]schema.ServiceInstance
+       request := graphql.NewRequest(`
+               query ($serviceId: ID!, $duration: Duration!) {
+                       instances: getServiceInstances(duration: $duration, 
serviceId: $serviceId) {
+                               id
+                               name
+                               language
+                               instanceUUID
+                               attributes {
+                                       name
+                                       value
+                               }
+                       }
+               }
+       `)
+       request.Var("serviceId", serviceID)
+       request.Var("duration", duration)
+
+       client.ExecuteQueryOrFail(cliCtx, request, &response)
+       return response["instances"]
+}
+
 func ServerTimeInfo(cliCtx *cli.Context) (schema.TimeInfo, error) {
+       var response map[string]schema.TimeInfo
        request := graphql.NewRequest(`
                query {
                        timeInfo: getTimeInfo {
@@ -34,7 +126,6 @@ func ServerTimeInfo(cliCtx *cli.Context) (schema.TimeInfo, 
error) {
                }
        `)
 
-       var response map[string]schema.TimeInfo
        if err := client.ExecuteQuery(cliCtx, request, &response); err != nil {
                return schema.TimeInfo{}, err
        }
diff --git a/graphql/metrics/metrics.go b/graphql/metrics/metrics.go
new file mode 100644
index 0000000..c086e51
--- /dev/null
+++ b/graphql/metrics/metrics.go
@@ -0,0 +1,82 @@
+// Licensed to 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. Apache Software Foundation (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.
+
+package metrics
+
+import (
+       "github.com/machinebox/graphql"
+       "github.com/urfave/cli"
+
+       "github.com/apache/skywalking-cli/graphql/client"
+
+       "github.com/apache/skywalking-cli/graphql/schema"
+)
+
+func IntValues(ctx *cli.Context, condition schema.BatchMetricConditions, 
duration schema.Duration) schema.IntValues {
+       var response map[string]schema.IntValues
+
+       request := graphql.NewRequest(`
+               query ($metric: BatchMetricConditions!, $duration: Duration!) {
+                       metrics: getValues(metric: $metric, duration: 
$duration) {
+                               values { id value }
+                       }
+               }
+       `)
+       request.Var("metric", condition)
+       request.Var("duration", duration)
+
+       client.ExecuteQueryOrFail(ctx, request, &response)
+
+       return response["metrics"]
+}
+
+func LinearIntValues(ctx *cli.Context, condition schema.MetricCondition, 
duration schema.Duration) schema.IntValues {
+       var response map[string]schema.IntValues
+
+       request := graphql.NewRequest(`
+               query ($metric: MetricCondition!, $duration: Duration!) {
+                       metrics: getLinearIntValues(metric: $metric, duration: 
$duration) {
+                               values { value }
+                       }
+               }
+       `)
+       request.Var("metric", condition)
+       request.Var("duration", duration)
+
+       client.ExecuteQueryOrFail(ctx, request, &response)
+
+       return response["metrics"]
+}
+
+func MultipleLinearIntValues(ctx *cli.Context, condition 
schema.MetricCondition, numOfLinear int, duration schema.Duration) 
[]schema.IntValues {
+       request := graphql.NewRequest(`
+               query ($metric: MetricCondition!, $numOfLinear: Int!, 
$duration: Duration!) {
+                       metrics: getMultipleLinearIntValues(metric: $metric, 
numOfLinear: $numOfLinear, duration: $duration) {
+                               values { value }
+                       }
+               }
+       `)
+       request.Var("metric", condition)
+       request.Var("numOfLinear", numOfLinear)
+       request.Var("duration", duration)
+
+       var response map[string][]schema.IntValues
+
+       client.ExecuteQueryOrFail(ctx, request, &response)
+
+       return response["metrics"]
+}
diff --git a/graphql/metadata/metadata.go b/graphql/utils/adapter.go
similarity index 61%
copy from graphql/metadata/metadata.go
copy to graphql/utils/adapter.go
index 55a1bc7..baec147 100644
--- a/graphql/metadata/metadata.go
+++ b/graphql/utils/adapter.go
@@ -15,28 +15,31 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package metadata
+package utils
 
 import (
-       "github.com/machinebox/graphql"
-       "github.com/urfave/cli"
+       "time"
 
-       "github.com/apache/skywalking-cli/graphql/client"
        "github.com/apache/skywalking-cli/graphql/schema"
+       "github.com/apache/skywalking-cli/logger"
 )
 
-func ServerTimeInfo(cliCtx *cli.Context) (schema.TimeInfo, error) {
-       request := graphql.NewRequest(`
-               query {
-                       timeInfo: getTimeInfo {
-                               timezone, currentTimestamp
-                       }
-               }
-       `)
-
-       var response map[string]schema.TimeInfo
-       if err := client.ExecuteQuery(cliCtx, request, &response); err != nil {
-               return schema.TimeInfo{}, err
+type IntValues schema.IntValues
+
+func MetricsToMap(duration schema.Duration, intValues schema.IntValues) 
map[string]float64 {
+       kvInts := intValues.Values
+       values := map[string]float64{}
+       format := StepFormats[duration.Step]
+       startTime, err := time.Parse(format, duration.Start)
+
+       if err != nil {
+               logger.Log.Fatalln(err)
        }
-       return response["timeInfo"], nil
+
+       step := StepDuration[duration.Step]
+       for idx, value := range kvInts {
+               values[startTime.Add(time.Duration(idx)*step).Format(format)] = 
float64(value.Value)
+       }
+
+       return values
 }
diff --git a/graphql/schema/constants.go b/graphql/utils/constants.go
similarity index 64%
rename from graphql/schema/constants.go
rename to graphql/utils/constants.go
index f146338..b4f0f3d 100644
--- a/graphql/schema/constants.go
+++ b/graphql/utils/constants.go
@@ -15,24 +15,28 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package schema
+package utils
 
-import "time"
+import (
+       "time"
+
+       "github.com/apache/skywalking-cli/graphql/schema"
+)
 
 // StepFormats is a mapping from schema.Step to its time format
-var StepFormats = map[Step]string{
-       StepSecond: "2006-01-02 150400",
-       StepMinute: "2006-01-02 1504",
-       StepHour:   "2006-01-02 15",
-       StepDay:    "2006-01-02",
-       StepMonth:  "2006-01",
+var StepFormats = map[schema.Step]string{
+       schema.StepSecond: "2006-01-02 150400",
+       schema.StepMinute: "2006-01-02 1504",
+       schema.StepHour:   "2006-01-02 15",
+       schema.StepDay:    "2006-01-02",
+       schema.StepMonth:  "2006-01",
 }
 
 // StepDuration is a mapping from schema.Step to its time.Duration
-var StepDuration = map[Step]time.Duration{
-       StepSecond: time.Second,
-       StepMinute: time.Minute,
-       StepHour:   time.Hour,
-       StepDay:    time.Hour * 24,
-       StepMonth:  time.Hour * 24 * 30,
+var StepDuration = map[schema.Step]time.Duration{
+       schema.StepSecond: time.Second,
+       schema.StepMinute: time.Minute,
+       schema.StepHour:   time.Hour,
+       schema.StepDay:    time.Hour * 24,
+       schema.StepMonth:  time.Hour * 24 * 30,
 }
diff --git a/swctl/main.go b/swctl/main.go
index 2efdee7..6c39a23 100644
--- a/swctl/main.go
+++ b/swctl/main.go
@@ -21,10 +21,9 @@ import (
        "io/ioutil"
        "os"
 
-       "github.com/apache/skywalking-cli/commands/endpoint"
-       linearMetrics "github.com/apache/skywalking-cli/commands/metrics/linear"
-       singleMetrics "github.com/apache/skywalking-cli/commands/metrics/single"
+       "github.com/apache/skywalking-cli/commands/metrics"
 
+       "github.com/apache/skywalking-cli/commands/endpoint"
        "github.com/apache/skywalking-cli/commands/instance"
 
        "github.com/sirupsen/logrus"
@@ -81,11 +80,10 @@ func main() {
        }
 
        app.Commands = []cli.Command{
-               service.Command,
-               instance.Command,
-               linearMetrics.Command,
-               singleMetrics.Command,
                endpoint.Command,
+               instance.Command,
+               service.Command,
+               metrics.Command,
        }
 
        app.Before = interceptor.BeforeChain([]cli.BeforeFunc{

Reply via email to