Add the sub-command `metrics nullable` for query the nullable metrics value (#176)

diff --git a/CHANGES.md b/CHANGES.md
index 97c1671..3b39d1d 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -37,6 +37,7 @@
 * Add the sub-command `profiling continuous` for adapt the new continuous profiling API by @mrproliu in https://github.com/apache/skywalking-cli/pull/173
 * Adapt the sub-command `metrics` for deprecate scope fron entity by @mrproliu in https://github.com/apache/skywalking-cli/pull/173
 * Add components in topology related sub-commands. @mrproliu in https://github.com/apache/skywalking-cli/pull/175
+* Add the sub-command `metrics nullable` for query the nullable metrics value. @mrproliu in https://github.com/apache/skywalking-cli/pull/176
 
 0.10.0
 ------------------
diff --git a/assets/graphqls/metrics/NullableMetricsValue.graphql b/assets/graphqls/metrics/NullableMetricsValue.graphql
new file mode 100644
index 0000000..c460d3f
--- /dev/null
+++ b/assets/graphqls/metrics/NullableMetricsValue.graphql
@@ -0,0 +1,22 @@
+# 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.
+
+query ($condition: MetricsCondition!, $duration: Duration!) {
+    result: readNullableMetricsValue(condition: $condition, duration: $duration) {
+        value
+    }
+}
diff --git a/dist/LICENSE b/dist/LICENSE
index 64ef910..157afc6 100644
--- a/dist/LICENSE
+++ b/dist/LICENSE
@@ -213,7 +213,7 @@
     k8s.io/utils v0.0.0-20210802155522-efc7438f0176 Apache-2.0
     sigs.k8s.io/controller-runtime v0.10.0 Apache-2.0
     sigs.k8s.io/structured-merge-diff/v4 v4.1.2 Apache-2.0
-    skywalking.apache.org/repo/goapi v0.0.0-20230301143132-aa3f8469385b Apache-2.0
+    skywalking.apache.org/repo/goapi v0.0.0-20230314034821-0c5a44bb767a Apache-2.0
 
 ========================================================================
 BSD-2-Clause licenses
@@ -250,7 +250,7 @@
     golang.org/x/term v0.5.0 BSD-3-Clause
     golang.org/x/text v0.7.0 BSD-3-Clause
     golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac BSD-3-Clause
-    google.golang.org/protobuf v1.28.1 BSD-3-Clause
+    google.golang.org/protobuf v1.29.0 BSD-3-Clause
     gopkg.in/inf.v0 v0.9.1 BSD-3-Clause
 
 ========================================================================
diff --git a/go.mod b/go.mod
index faffb39..edc7723 100644
--- a/go.mod
+++ b/go.mod
@@ -20,7 +20,7 @@
 	gopkg.in/yaml.v2 v2.4.0
 	k8s.io/apimachinery v0.22.1
 	sigs.k8s.io/controller-runtime v0.10.0
-	skywalking.apache.org/repo/goapi v0.0.0-20230301143132-aa3f8469385b
+	skywalking.apache.org/repo/goapi v0.0.0-20230314034821-0c5a44bb767a
 )
 
 require (
@@ -77,7 +77,7 @@
 	gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
 	google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84 // indirect
-	google.golang.org/protobuf v1.28.1 // indirect
+	google.golang.org/protobuf v1.29.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/ini.v1 v1.51.0 // indirect
 	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
diff --git a/go.sum b/go.sum
index 8584b61..a97d4e5 100644
--- a/go.sum
+++ b/go.sum
@@ -789,8 +789,8 @@
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
-google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.29.0 h1:44S3JjaKmLEE4YIkjzexaP+NzZsudE3Zin5Njn/pYX0=
+google.golang.org/protobuf v1.29.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -864,5 +864,5 @@
 sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
 sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
 sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
-skywalking.apache.org/repo/goapi v0.0.0-20230301143132-aa3f8469385b h1:VAWQr1mJk4P/a8VZ9UASY8H53wj0zdLHgYvhddyQcXw=
-skywalking.apache.org/repo/goapi v0.0.0-20230301143132-aa3f8469385b/go.mod h1:WovoDv1GA+8VuvHPVJL7q/fL0KlYPBZq5rTMCFQRzJU=
+skywalking.apache.org/repo/goapi v0.0.0-20230314034821-0c5a44bb767a h1:m8DTnaSEOEnPXRWmA6g7isbdqw7WPZP6SnaEHz1Sx7s=
+skywalking.apache.org/repo/goapi v0.0.0-20230314034821-0c5a44bb767a/go.mod h1:LcZMcxDjdJPn5yetydFnxe0l7rmiv8lvHEnzRbsey14=
diff --git a/internal/commands/metrics/metrics.go b/internal/commands/metrics/metrics.go
index 5ae661b..5141729 100644
--- a/internal/commands/metrics/metrics.go
+++ b/internal/commands/metrics/metrics.go
@@ -34,6 +34,7 @@
 	Usage: "Query metrics defined in backend OAL",
 	Subcommands: cli.Commands{
 		single.Command,
+		single.NullableCommand,
 		linear.Single,
 		linear.Multiple,
 		thermodynamic.Command,
diff --git a/internal/commands/metrics/single/nullable-metrics.go b/internal/commands/metrics/single/nullable-metrics.go
new file mode 100644
index 0000000..9b73aba
--- /dev/null
+++ b/internal/commands/metrics/single/nullable-metrics.go
@@ -0,0 +1,85 @@
+// 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 single
+
+import (
+	"github.com/urfave/cli/v2"
+
+	"github.com/apache/skywalking-cli/internal/commands/interceptor"
+	"github.com/apache/skywalking-cli/internal/flags"
+	"github.com/apache/skywalking-cli/internal/model"
+	"github.com/apache/skywalking-cli/pkg/display"
+	"github.com/apache/skywalking-cli/pkg/display/displayable"
+	"github.com/apache/skywalking-cli/pkg/graphql/metrics"
+
+	api "skywalking.apache.org/repo/goapi/query"
+)
+
+var NullableCommand = &cli.Command{
+	Name:  "nullable",
+	Usage: "query nullable-value metrics defined in backend OAL",
+	UsageText: `Query nullable-value metrics defined in backend OAL.
+
+Examples:
+1. Query the traffic load (calls per minute) of service "business-zone:projectC"
+$ swctl metrics nullable --name service_cpm --service-name business-zone::projectC
+
+2. Query the traffic load (calls per minute) of service "business-zone:projectC" endpoint "/projectC/{value}"
+$ swctl metrics nullable --name endpoint_cpm --service-name business-zone::projectC --endpoint-name /projectC/{value}`,
+	Flags: flags.Flags(
+		flags.DurationFlags,
+		flags.MetricsFlags,
+		flags.InstanceRelationFlags,
+		flags.EndpointRelationFlags,
+		flags.ProcessRelationFlags,
+	),
+	Before: interceptor.BeforeChain(
+		interceptor.DurationInterceptor,
+		interceptor.ParseEndpointRelation(false),
+		interceptor.ParseInstanceRelation(false),
+		interceptor.ParseProcessRelation(false),
+	),
+	Action: func(ctx *cli.Context) error {
+		end := ctx.String("end")
+		start := ctx.String("start")
+		step := ctx.Generic("step")
+
+		metricsName := ctx.String("name")
+		entity, err := interceptor.ParseEntity(ctx)
+		if err != nil {
+			return err
+		}
+
+		duration := api.Duration{
+			Start: start,
+			End:   end,
+			Step:  step.(*model.StepEnumValue).Selected,
+		}
+
+		metricsValue, err := metrics.NullableIntValue(ctx, api.MetricsCondition{
+			Name:   metricsName,
+			Entity: entity,
+		}, duration)
+
+		if err != nil {
+			return err
+		}
+
+		return display.Display(ctx, &displayable.Displayable{Data: metricsValue.Value})
+	},
+}
diff --git a/pkg/graphql/metrics/metrics.go b/pkg/graphql/metrics/metrics.go
index 870c667..44f45c7 100644
--- a/pkg/graphql/metrics/metrics.go
+++ b/pkg/graphql/metrics/metrics.go
@@ -40,6 +40,19 @@
 	return response["result"], err
 }
 
+func NullableIntValue(ctx *cli.Context, condition api.MetricsCondition, duration api.Duration) (api.NullableValue, error) {
+	var response map[string]api.NullableValue
+
+	request := graphql.NewRequest(assets.Read("graphqls/metrics/NullableMetricsValue.graphql"))
+
+	request.Var("condition", condition)
+	request.Var("duration", duration)
+
+	err := client.ExecuteQuery(ctx, request, &response)
+
+	return response["result"], err
+}
+
 func LinearIntValues(ctx *cli.Context, condition api.MetricsCondition, duration api.Duration) (api.MetricsValues, error) {
 	var response map[string]api.MetricsValues